From fa4f73b92f5646ba6365789ac44ead0f7e6ad6e8 Mon Sep 17 00:00:00 2001 From: cl507523 Date: Tue, 14 Apr 2026 12:22:50 +0000 Subject: [PATCH] fix(runtime): harden trap jumps and bound safe LEB reads Switch trap recovery to sigsetjmp/siglongjmp so signal masks are restored after CPU traps, and add explicit end-bounded decoding in readSafeLEBNumber with call-site updates across bytecode visitors and interpreter paths. Made-with: Cursor --- src/action/bytecode_visitor.h | 90 +++++++++++++++++------------------ src/action/interpreter.cpp | 44 ++++++++--------- src/common/evm_traphandler.h | 10 ++-- src/common/traphandler.h | 8 ++-- src/runtime/runtime.cpp | 14 ++++-- src/utils/wasm.h | 20 +++++++- 6 files changed, 104 insertions(+), 82 deletions(-) diff --git a/src/action/bytecode_visitor.h b/src/action/bytecode_visitor.h index 5d22dd711..f07d9a596 100644 --- a/src/action/bytecode_visitor.h +++ b/src/action/bytecode_visitor.h @@ -135,19 +135,19 @@ template class WASMByteCodeVisitor { break; case Opcode::BR: - Ip = readSafeLEBNumber(Ip, U32); + Ip = readSafeLEBNumber(Ip, IpEnd, U32); handleBranch(U32); Ip = skipCurrentBlock(Ip, IpEnd); CurBlock.setReachable(false); break; case Opcode::BR_IF: - Ip = readSafeLEBNumber(Ip, U32); + Ip = readSafeLEBNumber(Ip, IpEnd, U32); handleBranchIf(U32); break; case Opcode::BR_TABLE: - Ip = readSafeLEBNumber(Ip, U32); + Ip = readSafeLEBNumber(Ip, IpEnd, U32); Ip = handleBranchTable(Ip, IpEnd, U32); Ip = skipCurrentBlock(Ip, IpEnd); CurBlock.setReachable(false); @@ -160,7 +160,7 @@ template class WASMByteCodeVisitor { break; case Opcode::CALL: { - Ip = readSafeLEBNumber(Ip, U32); + Ip = readSafeLEBNumber(Ip, IpEnd, U32); if (U32 == CurMod->getGasFuncIdx()) { handleGasCall(); break; @@ -180,7 +180,7 @@ template class WASMByteCodeVisitor { } case Opcode::CALL_INDIRECT: - Ip = readSafeLEBNumber(Ip, U32); + Ip = readSafeLEBNumber(Ip, IpEnd, U32); Ip++; // Skip table index(0) handleCallIndirect(U32, 0); break; @@ -196,119 +196,119 @@ template class WASMByteCodeVisitor { break; case Opcode::GET_LOCAL: - Ip = readSafeLEBNumber(Ip, U32); + Ip = readSafeLEBNumber(Ip, IpEnd, U32); handleGetLocal(U32); break; case Opcode::SET_LOCAL: - Ip = readSafeLEBNumber(Ip, U32); + Ip = readSafeLEBNumber(Ip, IpEnd, U32); handleSetLocal(U32); break; case Opcode::TEE_LOCAL: - Ip = readSafeLEBNumber(Ip, U32); + Ip = readSafeLEBNumber(Ip, IpEnd, U32); handleTeeLocal(U32); break; case Opcode::GET_GLOBAL: - Ip = readSafeLEBNumber(Ip, U32); + Ip = readSafeLEBNumber(Ip, IpEnd, U32); handleGetGlobal(U32); break; case Opcode::SET_GLOBAL: - Ip = readSafeLEBNumber(Ip, U32); + Ip = readSafeLEBNumber(Ip, IpEnd, U32); handleSetGlobal(U32); break; case Opcode::I32_LOAD: - Ip = handleLoad(Ip); + Ip = handleLoad(Ip, IpEnd); break; case Opcode::I32_LOAD8_S: - Ip = handleLoad(Ip); + Ip = handleLoad(Ip, IpEnd); break; case Opcode::I32_LOAD8_U: - Ip = handleLoad(Ip); + Ip = handleLoad(Ip, IpEnd); break; case Opcode::I32_LOAD16_S: - Ip = handleLoad(Ip); + Ip = handleLoad(Ip, IpEnd); break; case Opcode::I32_LOAD16_U: - Ip = handleLoad(Ip); + Ip = handleLoad(Ip, IpEnd); break; case Opcode::I64_LOAD: - Ip = handleLoad(Ip); + Ip = handleLoad(Ip, IpEnd); break; case Opcode::I64_LOAD8_S: - Ip = handleLoad(Ip); + Ip = handleLoad(Ip, IpEnd); break; case Opcode::I64_LOAD8_U: - Ip = handleLoad(Ip); + Ip = handleLoad(Ip, IpEnd); break; case Opcode::I64_LOAD16_S: - Ip = handleLoad(Ip); + Ip = handleLoad(Ip, IpEnd); break; case Opcode::I64_LOAD16_U: - Ip = handleLoad(Ip); + Ip = handleLoad(Ip, IpEnd); break; case Opcode::I64_LOAD32_S: - Ip = handleLoad(Ip); + Ip = handleLoad(Ip, IpEnd); break; case Opcode::I64_LOAD32_U: - Ip = handleLoad(Ip); + Ip = handleLoad(Ip, IpEnd); break; case Opcode::F32_LOAD: - Ip = handleLoad(Ip); + Ip = handleLoad(Ip, IpEnd); break; case Opcode::F64_LOAD: - Ip = handleLoad(Ip); + Ip = handleLoad(Ip, IpEnd); break; case Opcode::I32_STORE: - Ip = handleStore(Ip); + Ip = handleStore(Ip, IpEnd); break; case Opcode::I32_STORE8: - Ip = handleStore(Ip); + Ip = handleStore(Ip, IpEnd); break; case Opcode::I32_STORE16: - Ip = handleStore(Ip); + Ip = handleStore(Ip, IpEnd); break; case Opcode::I64_STORE: - Ip = handleStore(Ip); + Ip = handleStore(Ip, IpEnd); break; case Opcode::I64_STORE8: - Ip = handleStore(Ip); + Ip = handleStore(Ip, IpEnd); break; case Opcode::I64_STORE16: - Ip = handleStore(Ip); + Ip = handleStore(Ip, IpEnd); break; case Opcode::I64_STORE32: - Ip = handleStore(Ip); + Ip = handleStore(Ip, IpEnd); break; case Opcode::F32_STORE: - Ip = handleStore(Ip); + Ip = handleStore(Ip, IpEnd); break; case Opcode::F64_STORE: - Ip = handleStore(Ip); + Ip = handleStore(Ip, IpEnd); break; case Opcode::MEMORY_SIZE: // Skip the memory index(0) - Ip = readSafeLEBNumber(Ip, U32); + Ip = readSafeLEBNumber(Ip, IpEnd, U32); handleMemorySize(); break; case Opcode::MEMORY_GROW: // Skip the memory index(0) - Ip = readSafeLEBNumber(Ip, U32); + Ip = readSafeLEBNumber(Ip, IpEnd, U32); handleMemoryGrow(); break; case Opcode::I32_CONST: - Ip = readSafeLEBNumber(Ip, I32); + Ip = readSafeLEBNumber(Ip, IpEnd, I32); handleConst(I32); break; case Opcode::I64_CONST: - Ip = readSafeLEBNumber(Ip, I64); + Ip = readSafeLEBNumber(Ip, IpEnd, I64); handleConst(I64); break; case Opcode::F32_CONST: @@ -820,7 +820,7 @@ template class WASMByteCodeVisitor { WASMType Type = WASMType::VOID; for (uint32_t I = 0; I < Count + 1; ++I) { uint32_t TargetLevel; - Ip = readSafeLEBNumber(Ip, TargetLevel); + Ip = readSafeLEBNumber(Ip, End, TargetLevel); if (Ip >= End) { break; } @@ -943,11 +943,11 @@ template class WASMByteCodeVisitor { // ==================== Memory Instruction Handlers ==================== template - const uint8_t *handleLoad(const uint8_t *Ip) { + const uint8_t *handleLoad(const uint8_t *Ip, const uint8_t *End) { uint32_t Align; uint32_t Offset; - Ip = readSafeLEBNumber(Ip, Align); - Ip = readSafeLEBNumber(Ip, Offset); + Ip = readSafeLEBNumber(Ip, End, Align); + Ip = readSafeLEBNumber(Ip, End, Offset); Operand Base = pop(); Operand Result = Builder.template handleLoad( Base, Offset, Align); @@ -956,11 +956,11 @@ template class WASMByteCodeVisitor { } template - const uint8_t *handleStore(const uint8_t *Ip) { + const uint8_t *handleStore(const uint8_t *Ip, const uint8_t *End) { uint32_t Align; uint32_t Offset; - Ip = readSafeLEBNumber(Ip, Align); - Ip = readSafeLEBNumber(Ip, Offset); + Ip = readSafeLEBNumber(Ip, End, Align); + Ip = readSafeLEBNumber(Ip, End, Offset); Operand Value = pop(); Operand Base = pop(); ZEN_ASSERT(Value.getType() == SrcType); @@ -1015,7 +1015,7 @@ template class WASMByteCodeVisitor { } case Opcode::BR_IF: { - Ip = readSafeLEBNumber(Ip + 1, U32Val); + Ip = readSafeLEBNumber(Ip + 1, End, U32Val); // bounds check if (Ip < End) { const CtrlBlockInfo &Info = Builder.getBlockInfo(U32Val); diff --git a/src/action/interpreter.cpp b/src/action/interpreter.cpp index e4551bb5d..f7d896e40 100644 --- a/src/action/interpreter.cpp +++ b/src/action/interpreter.cpp @@ -364,7 +364,7 @@ class BaseInterpreterImpl { break; case BR_TABLE: { uint32_t NumTargets = 0; - Ptr = readSafeLEBNumber(Ptr, NumTargets); + Ptr = readSafeLEBNumber(Ptr, End, NumTargets); for (uint32_t I = 0; I <= NumTargets; ++I) { Ptr = skipLEBNumber(Ptr, End); } @@ -905,8 +905,8 @@ class BaseInterpreterImpl { InterpFrame *Frame, uint32_t *&ValStackPtr, uint64_t LinearMemSize) { uint32_t Align, Offset; - Ip = readSafeLEBNumber(Ip, Align); - Ip = readSafeLEBNumber(Ip, Offset); + Ip = readSafeLEBNumber(Ip, IpEnd, Align); + Ip = readSafeLEBNumber(Ip, IpEnd, Offset); SrcType Val = Frame->valuePop(ValStackPtr); uint32_t Addr = Frame->valuePop(ValStackPtr); if ((uint64_t)Offset + sizeof(DestType) + Addr > LinearMemSize) { @@ -925,8 +925,8 @@ class BaseInterpreterImpl { InterpFrame *Frame, uint32_t *&ValStackPtr, uint64_t LinearMemSize) { uint32_t Align, Offset; - Ip = readSafeLEBNumber(Ip, Align); - Ip = readSafeLEBNumber(Ip, Offset); + Ip = readSafeLEBNumber(Ip, IpEnd, Align); + Ip = readSafeLEBNumber(Ip, IpEnd, Offset); uint32_t Addr = Frame->valuePop(ValStackPtr); if ((uint64_t)Offset + sizeof(SrcType) + Addr > LinearMemSize) { throw getError(ErrorCode::OutOfBoundsMemory); @@ -1184,12 +1184,12 @@ void BaseInterpreterImpl::interpret() { BREAK; } CASE(BR) : { - Ip = readSafeLEBNumber(Ip, Depth); + Ip = readSafeLEBNumber(Ip, IpEnd, Depth); Frame->blockPop(ControlStackPtr, ValStackPtr, Ip, Depth); BREAK; } CASE(BR_IF) : { - Ip = readSafeLEBNumber(Ip, Depth); + Ip = readSafeLEBNumber(Ip, IpEnd, Depth); Cond = Frame->valuePop(ValStackPtr); if (Cond) { Frame->blockPop(ControlStackPtr, ValStackPtr, Ip, Depth); @@ -1198,13 +1198,13 @@ void BaseInterpreterImpl::interpret() { } CASE(BR_TABLE) : { uint32_t Count; - Ip = readSafeLEBNumber(Ip, Count); + Ip = readSafeLEBNumber(Ip, IpEnd, Count); uint32_t LabelIdx = std::min(Count, Frame->valuePop(ValStackPtr)); for (uint32_t I = 0; I < LabelIdx; I++) { Ip = skipLEBNumber(Ip, IpEnd); } - Ip = readSafeLEBNumber(Ip, Depth); + Ip = readSafeLEBNumber(Ip, IpEnd, Depth); Frame->blockPop(ControlStackPtr, ValStackPtr, Ip, Depth); BREAK; } @@ -1240,19 +1240,19 @@ void BaseInterpreterImpl::interpret() { BREAK; } CASE(GET_GLOBAL_64) : { - Ip = readSafeLEBNumber(Ip, GlobalIdx); + Ip = readSafeLEBNumber(Ip, IpEnd, GlobalIdx); uint8_t *GlobalAddr = ModInst->getGlobalAddr(GlobalIdx); Frame->valuePush(ValStackPtr, *(int64_t *)GlobalAddr); BREAK; } CASE(SET_GLOBAL_64) : { - Ip = readSafeLEBNumber(Ip, GlobalIdx); + Ip = readSafeLEBNumber(Ip, IpEnd, GlobalIdx); uint8_t *GlobalAddr = ModInst->getGlobalAddr(GlobalIdx); *(int64_t *)GlobalAddr = Frame->valuePop(ValStackPtr); BREAK; } CASE(GET_LOCAL) : { - Ip = readSafeLEBNumber(Ip, LocalIdx); + Ip = readSafeLEBNumber(Ip, IpEnd, LocalIdx); LocalType = FuncInst->getLocalType(LocalIdx); LocalOffset = FuncInst->getLocalOffset(LocalIdx); @@ -1276,7 +1276,7 @@ void BaseInterpreterImpl::interpret() { BREAK; } CASE(SET_LOCAL) : { - Ip = readSafeLEBNumber(Ip, LocalIdx); + Ip = readSafeLEBNumber(Ip, IpEnd, LocalIdx); LocalType = FuncInst->getLocalType(LocalIdx); LocalOffset = FuncInst->getLocalOffset(LocalIdx); @@ -1297,7 +1297,7 @@ void BaseInterpreterImpl::interpret() { BREAK; } CASE(TEE_LOCAL) : { - Ip = readSafeLEBNumber(Ip, LocalIdx); + Ip = readSafeLEBNumber(Ip, IpEnd, LocalIdx); LocalType = FuncInst->getLocalType(LocalIdx); LocalOffset = FuncInst->getLocalOffset(LocalIdx); @@ -1318,7 +1318,7 @@ void BaseInterpreterImpl::interpret() { BREAK; } CASE(GET_GLOBAL) : { - Ip = readSafeLEBNumber(Ip, GlobalIdx); + Ip = readSafeLEBNumber(Ip, IpEnd, GlobalIdx); uint8_t *GlobalAddr = ModInst->getGlobalAddr(GlobalIdx); WASMType GlobalType = ModInst->getGlobalType(GlobalIdx); switch (GlobalType) { @@ -1336,7 +1336,7 @@ void BaseInterpreterImpl::interpret() { BREAK; } CASE(SET_GLOBAL) : { - Ip = readSafeLEBNumber(Ip, GlobalIdx); + Ip = readSafeLEBNumber(Ip, IpEnd, GlobalIdx); uint8_t *GlobalAddr = ModInst->getGlobalAddr(GlobalIdx); WASMType GlobalType = ModInst->getGlobalType(GlobalIdx); switch (GlobalType) { @@ -1361,7 +1361,7 @@ void BaseInterpreterImpl::interpret() { } CASE(I32_CONST) : { int32_t I32Const; - Ip = readSafeLEBNumber(Ip, I32Const); + Ip = readSafeLEBNumber(Ip, IpEnd, I32Const); Frame->valuePush(ValStackPtr, I32Const); BREAK; } @@ -1373,12 +1373,12 @@ void BaseInterpreterImpl::interpret() { } CASE(I64_CONST) : { int64_t I64Const; - Ip = readSafeLEBNumber(Ip, I64Const); + Ip = readSafeLEBNumber(Ip, IpEnd, I64Const); Frame->valuePush(ValStackPtr, I64Const); BREAK; } CASE(MEMORY_GROW) : { - Ip = readSafeLEBNumber(Ip, LocalIdx); + Ip = readSafeLEBNumber(Ip, IpEnd, LocalIdx); uint32_t GrowOldPageCount = Memory->CurPages; uint32_t GrowPageCount = Frame->valuePop(ValStackPtr); @@ -1391,7 +1391,7 @@ void BaseInterpreterImpl::interpret() { BREAK; } CASE(MEMORY_SIZE) : { - Ip = readSafeLEBNumber(Ip, LocalIdx); + Ip = readSafeLEBNumber(Ip, IpEnd, LocalIdx); Frame->valuePush(ValStackPtr, Memory->CurPages); BREAK; } @@ -2008,7 +2008,7 @@ void BaseInterpreterImpl::interpret() { BREAK; } CASE(CALL) : { - Ip = readSafeLEBNumber(Ip, FuncIdx); + Ip = readSafeLEBNumber(Ip, IpEnd, FuncIdx); #ifdef ZEN_ENABLE_DEBUG_INTERP ZEN_LOG_DEBUG("fidx: %d", FuncIdx); #endif @@ -2040,7 +2040,7 @@ void BaseInterpreterImpl::interpret() { CASE(CALL_INDIRECT) : { uint32_t TypeIdx = 0, TableIdx = 0; - Ip = readSafeLEBNumber(Ip, TypeIdx); + Ip = readSafeLEBNumber(Ip, IpEnd, TypeIdx); // Skip the fixed byte for `table 0` ++Ip; auto *ExpectedFuncType = Mod->getDeclaredType(TypeIdx); diff --git a/src/common/evm_traphandler.h b/src/common/evm_traphandler.h index b801d91c2..3008ecb5d 100644 --- a/src/common/evm_traphandler.h +++ b/src/common/evm_traphandler.h @@ -40,8 +40,8 @@ class EVMCallThreadState { typedef void (*SigActionHandlerType)(int, siginfo_t *, void *); public: - EVMCallThreadState(runtime::EVMInstance *Inst, jmp_buf *Env, void *FrameAddr, - void *PC = nullptr) + EVMCallThreadState(runtime::EVMInstance *Inst, sigjmp_buf *Env, + void *FrameAddr, void *PC = nullptr) : Inst(Inst) { StartFrame = {.PC = PC, .FrameAddr = FrameAddr}; Parent = currentThreadStateOrUpdate(nullptr, false); @@ -69,7 +69,7 @@ class EVMCallThreadState { EVMCallThreadState *parent() const { return Parent; } - jmp_buf *jmpbuf() { return JmpBuf; } + sigjmp_buf *jmpbuf() { return JmpBuf; } void setHandler(SigActionHandlerType Handler) { restartHandler(); } @@ -77,7 +77,7 @@ class EVMCallThreadState { void restartHandler() { Handling = true; } - void jmpToMarked(int Signum) { longjmp(*JmpBuf, Signum); } + void jmpToMarked(int Signum) { siglongjmp(*JmpBuf, Signum); } void setTrapFrameAddr(void *Addr, void *PC, void *FaultingAddress, uint32_t NumIgnoredFrames) { @@ -127,7 +127,7 @@ class EVMCallThreadState { EVMCallThreadState *Parent = nullptr; bool Handling = false; - jmp_buf *JmpBuf = nullptr; // must be default nullptr + sigjmp_buf *JmpBuf = nullptr; // must be default nullptr // the frames count to ignore when dump wasm call stack by trap_frame_addr_ uint32_t NumIgnoredTrapFrames = 0; // the frame address(rbp register) when trap happen diff --git a/src/common/traphandler.h b/src/common/traphandler.h index 4aa0caad9..19bd71a4e 100644 --- a/src/common/traphandler.h +++ b/src/common/traphandler.h @@ -47,7 +47,7 @@ class CallThreadState { typedef void (*SigActionHandlerType)(int, siginfo_t *, void *); public: - CallThreadState(runtime::Instance *Inst, jmp_buf *Env, void *FrameAddr, + CallThreadState(runtime::Instance *Inst, sigjmp_buf *Env, void *FrameAddr, void *PC = nullptr) : Inst(Inst) { StartFrame = {.PC = PC, .FrameAddr = FrameAddr}; @@ -77,7 +77,7 @@ class CallThreadState { CallThreadState *parent() const { return Parent; } - jmp_buf *jmpbuf() { return JmpBuf; } + sigjmp_buf *jmpbuf() { return JmpBuf; } void setHandler(SigActionHandlerType Handler) { restartHandler(); } @@ -85,7 +85,7 @@ class CallThreadState { void restartHandler() { Handling = true; } - void jmpToMarked(int Signum) { longjmp(*JmpBuf, Signum); } + void jmpToMarked(int Signum) { siglongjmp(*JmpBuf, Signum); } void setTrapFrameAddr(void *Addr, void *PC, void *FaultingAddress, uint32_t NumIgnoredFrames) { @@ -135,7 +135,7 @@ class CallThreadState { CallThreadState *Parent = nullptr; bool Handling = false; - jmp_buf *JmpBuf = nullptr; // must be default nullptr + sigjmp_buf *JmpBuf = nullptr; // must be default nullptr // the frames count to ignore when dump wasm call stack by trap_frame_addr_ uint32_t NumIgnoredTrapFrames = 0; // the frame address(rbp register) when trap happen diff --git a/src/runtime/runtime.cpp b/src/runtime/runtime.cpp index 82896719b..71c5a8c11 100644 --- a/src/runtime/runtime.cpp +++ b/src/runtime/runtime.cpp @@ -780,14 +780,17 @@ void Runtime::callWasmFunctionInJITMode(Instance &Inst, uint32_t FuncIdx, GenericFunctionPointer(IsImport ? Func->CodePtr : Func->JITCodePtr); #ifdef ZEN_ENABLE_CPU_EXCEPTION - jmp_buf JmpBuf; + sigjmp_buf JmpBuf; common::traphandler::CallThreadState TLS(&Inst, &JmpBuf, __builtin_frame_address(0), nullptr); + // siglongjmp from signal handlers skips C++ unwinding on intermediate frames. + // Keep this wrapper minimal and avoid introducing non-trivial RAII objects + // between sigsetjmp() and callNativeGeneral(). // longjmp with asan(in gcc-9) not works well, it affects the asan stack // malloc. so use wrapper func to recover the stack auto CallWasmFnWrapper = [&]() { - int JmpSignum = ::setjmp(JmpBuf); + int JmpSignum = ::sigsetjmp(JmpBuf, 1); if (JmpSignum == 0) { TLS.restartHandler(); @@ -898,14 +901,17 @@ void Runtime::callEVMInJITMode(EVMInstance &Inst, evmc_message &Msg, }; #ifdef ZEN_ENABLE_CPU_EXCEPTION - jmp_buf JmpBuf; + sigjmp_buf JmpBuf; common::evm_traphandler::EVMCallThreadState TLS(&Inst, &JmpBuf, __builtin_frame_address(0)); + // siglongjmp from signal handlers skips C++ unwinding on intermediate frames. + // Keep this wrapper minimal and avoid introducing non-trivial RAII objects + // between sigsetjmp() and callNativeGeneral(). // longjmp with asan(in gcc-9) not works well, it affects the asan stack // malloc. so use wrapper func to recover the stack auto CallEVMFnWrapper = [&]() { - int JmpSignum = ::setjmp(JmpBuf); + int JmpSignum = ::sigsetjmp(JmpBuf, 1); if (JmpSignum == 0) { TLS.restartHandler(); #endif // ZEN_ENABLE_CPU_EXCEPTION diff --git a/src/utils/wasm.h b/src/utils/wasm.h index 14bc34bef..6a28d1a40 100644 --- a/src/utils/wasm.h +++ b/src/utils/wasm.h @@ -73,21 +73,37 @@ const uint8_t *readLEBNumber(const uint8_t *Ip, const uint8_t *End, T &Value) { } template -static const uint8_t *readSafeLEBNumber(const uint8_t *Ip, T &Value) { +static const uint8_t *readSafeLEBNumber(const uint8_t *Ip, const uint8_t *End, + T &Value) { + using common::ErrorCode; + using common::getError; + + if (Ip >= End) { + throw getError(ErrorCode::UnexpectedEnd); + } + + constexpr int MaxBytes = (sizeof(T) * 8 + 6) / 7; constexpr bool IsSigned = std::is_signed::value; T Result = 0; uint32_t Shift = 0; + uint32_t Count = 0; uint8_t Byte = 0; - while (true) { + while (Ip < End && Count < MaxBytes) { Byte = *Ip++; Result |= ((T)(Byte & 0x7f)) << Shift; Shift += 7; + Count++; if ((Byte & 0x80) == 0) { break; } } + if ((Byte & 0x80) != 0) { + throw getError(Ip >= End ? ErrorCode::UnexpectedEnd + : ErrorCode::LEBIntTooLong); + } + if constexpr (IsSigned) { if (Shift < (sizeof(T) * 8)) { if (Byte & 0x40) {