From c611d83c4e41bc5d8657a771c054f5b41a1efd77 Mon Sep 17 00:00:00 2001 From: lihehe Date: Thu, 29 Feb 2024 14:52:09 +0800 Subject: [PATCH] support jit code signing Change-Id: I457aaac0e721ae77d9d0fd3480cc9bfcee50baf8 Signed-off-by: lihehe --- BUILD.gn | 1 + bundle.json | 7 + interfaces/innerkits/common/include/errcode.h | 7 + interfaces/innerkits/jit_code_sign/BUILD.gn | 43 ++ .../include/jit_buffer_integrity.h | 98 +++++ .../jit_code_sign/include/jit_code_signer.h | 57 +++ .../jit_code_sign/include/pac_sign_ctx.h | 62 +++ .../jit_code_sign/include/random_helper.h | 76 ++++ .../jit_code_sign/src/jit_code_signer.cpp | 234 ++++++++++ .../jit_code_sign/src/pac_sign_ctx.cpp | 180 ++++++++ .../innerkits/jit_code_sign/src/test_src.cpp | 47 ++ test/unittest/BUILD.gn | 17 + test/unittest/jit_code_sign_test.cpp | 413 ++++++++++++++++++ 13 files changed, 1242 insertions(+) create mode 100644 interfaces/innerkits/jit_code_sign/BUILD.gn create mode 100644 interfaces/innerkits/jit_code_sign/include/jit_buffer_integrity.h create mode 100644 interfaces/innerkits/jit_code_sign/include/jit_code_signer.h create mode 100644 interfaces/innerkits/jit_code_sign/include/pac_sign_ctx.h create mode 100644 interfaces/innerkits/jit_code_sign/include/random_helper.h create mode 100644 interfaces/innerkits/jit_code_sign/src/jit_code_signer.cpp create mode 100644 interfaces/innerkits/jit_code_sign/src/pac_sign_ctx.cpp create mode 100644 interfaces/innerkits/jit_code_sign/src/test_src.cpp create mode 100644 test/unittest/jit_code_sign_test.cpp diff --git a/BUILD.gn b/BUILD.gn index f745cbf..74fcb02 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -26,6 +26,7 @@ config("common_utils_config") { group("subcomponents") { deps = [ "${code_signature_root_dir}/interfaces/innerkits/code_sign_attr_utils:libcode_sign_attr_utils", + "${code_signature_root_dir}/interfaces/innerkits/jit_code_sign:libjit_code_sign", "${code_signature_root_dir}/interfaces/innerkits/code_sign_utils:libcode_sign_utils", "${code_signature_root_dir}/interfaces/innerkits/local_code_sign:liblocal_code_sign_sdk", "${code_signature_root_dir}/services/key_enable:key_enable_targets", diff --git a/bundle.json b/bundle.json index e899a9e..b541be8 100644 --- a/bundle.json +++ b/bundle.json @@ -70,6 +70,13 @@ "header_files": [], "header_base": "//base/security/code_signature/interfaces/innerkits/code_sign_attr_utils/include" } + }, + { + "name" : "//base/security/code_signature/interfaces/innerkits/jit_code_sign:libjit_code_sign", + "header": { + "header_files": [], + "header_base": "//base/security/code_signature/interfaces/innerkits/jit_code_sign/include" + } } ], "test": [ "//base/security/code_signature/test:testgroup" ] diff --git a/interfaces/innerkits/common/include/errcode.h b/interfaces/innerkits/common/include/errcode.h index 0dd77c2..fd56151 100644 --- a/interfaces/innerkits/common/include/errcode.h +++ b/interfaces/innerkits/common/include/errcode.h @@ -97,4 +97,11 @@ enum SignBlockErrCode { CS_ERR_SIGN_ADDR_ALIGN = -0x621, CS_ERR_SIGN_EXTENSION_OFFSET_ALIGN = -0x622, }; + +enum JitCodeSignErrCode { + CS_ERR_PATCH_INVALID = -0x700, + CS_ERR_JIT_SIGN_SIZE = -0x701, + CS_ERR_TMP_BUFFER = -0x702, + CS_ERR_VALIDATE_CODE = -0x703, +}; #endif diff --git a/interfaces/innerkits/jit_code_sign/BUILD.gn b/interfaces/innerkits/jit_code_sign/BUILD.gn new file mode 100644 index 0000000..6af769f --- /dev/null +++ b/interfaces/innerkits/jit_code_sign/BUILD.gn @@ -0,0 +1,43 @@ +# Copyright (c) 2023 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/ohos.gni") +import("../../../code_signature.gni") + +config("public_jit_code_sign_configs") { + include_dirs = [ "include" ] +} + +ohos_shared_library("libjit_code_sign") { + sources = [ + "src/pac_sign_ctx.cpp", + "src/jit_code_signer.cpp" + ] + sanitize = { + cfi = true + cfi_cross_dso = true + debug = false + } + cflags = [ "-march=armv8.4-a" ] + public_configs = [ + ":public_jit_code_sign_configs", + "${code_signature_root_dir}:common_public_config", + ] + configs = [ "${code_signature_root_dir}:common_utils_config" ] + + external_deps = [ + "hilog:libhilog" + ] + part_name = "code_signature" + subsystem_name = "security" +} \ No newline at end of file diff --git a/interfaces/innerkits/jit_code_sign/include/jit_buffer_integrity.h b/interfaces/innerkits/jit_code_sign/include/jit_buffer_integrity.h new file mode 100644 index 0000000..b8bff7f --- /dev/null +++ b/interfaces/innerkits/jit_code_sign/include/jit_buffer_integrity.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CODE_SIGN_JIT_BUFFER_INTEGRITY_H +#define CODE_SIGN_JIT_BUFFER_INTEGRITY_H + +#include +#include + +#include "errcode.h" +#include "jit_code_signer.h" +#include "securec.h" + +namespace OHOS { +namespace Security { +namespace CodeSign { + +#define CAST_TO_CONST_BYTES(buffer) reinterpret_cast(buffer) +#define CAST_TO_BYTES(buffer) reinterpret_cast(buffer) + +static inline void RegisterTmpBuffer(JitCodeSigner *signer, void *tmpBuffer) +{ + signer->RegisterTmpBuffer(CAST_TO_BYTES(tmpBuffer)); +} + +static inline void AppendInstruction(JitCodeSigner *signer, Instr instr) +{ + signer->SignInstruction(instr); +} + +static inline void AppendData(JitCodeSigner *signer, const void *const data, uint32_t size) +{ + signer->SignData(CAST_TO_CONST_BYTES(data), size); +} + +static inline void WillFixUp(JitCodeSigner *signer, int n) +{ + signer->SkipNext(n); +} + +static inline void PatchInstruction(JitCodeSigner *signer, int offset, Instr instr) +{ + signer->PatchInstruction(offset, instr); +} + +static inline int32_t PatchData(JitCodeSigner *signer, int offset, const void *const data, uint32_t size) +{ + return signer->PatchData(offset, CAST_TO_CONST_BYTES(data), size); +} + +static inline int32_t ResetJitCacheOnly(void *jitMemory, int size) +{ + //prctl + int ret = memset_s(jitMemory, size, 0, size); + //prctl + return ret; +} + +static inline int32_t ResetJitCache(JitCodeSigner *signer, void *jitMemory, void *tmpBuffer, int size) +{ + signer->Reset(); + if (tmpBuffer != nullptr) { + signer->RegisterTmpBuffer(reinterpret_cast(tmpBuffer)); + } + return ResetJitCacheOnly(jitMemory, size); +} + +static inline int32_t CopyToJitCache(JitCodeSigner *signer, void *jitMemory, const void *tmpBuffer, int size) +{ + int32_t ret; + ret = signer->ValidateCodeCopy(reinterpret_cast(tmpBuffer), size); + if (ret != CS_SUCCESS) { + return ret; + } + // prctl + ret = memcpy_s(jitMemory, size, tmpBuffer, size); + if (ret != EOK) { + return CS_ERR_MEMORY; + } + // prctl + return CS_SUCCESS; +} +} +} +} +#endif \ No newline at end of file diff --git a/interfaces/innerkits/jit_code_sign/include/jit_code_signer.h b/interfaces/innerkits/jit_code_sign/include/jit_code_signer.h new file mode 100644 index 0000000..607ea57 --- /dev/null +++ b/interfaces/innerkits/jit_code_sign/include/jit_code_signer.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CODE_SIGN_JIT_CODE_SIGNER_H +#define CODE_SIGN_JIT_CODE_SIGNER_H + +#include +#include +#include "pac_sign_ctx.h" + +namespace OHOS { +namespace Security { +namespace CodeSign { +using Instr = uint32_t; +using Byte = uint8_t; + +class JitCodeSigner { +public: + JitCodeSigner(); + void Reset(); + void RegisterTmpBuffer(Byte *tmpBuffer); + void SignInstruction(Instr insn); + void SignData(const Byte *const data, uint32_t size); + void SkipNext(int n); + int32_t PatchInstruction(int offset, Instr insn); + int32_t PatchInstruction(Byte *buffer, Instr insn); + int32_t PatchData(int offset, const Byte *const data, uint32_t size); + + int32_t AfterPatchingInstructions(int offset, uint32_t n); + int32_t AfterPatchingInstructions(Byte *buffer, uint32_t); + int32_t ValidateCodeCopy(const Byte *jitBuffer, int size); +private: + int32_t ValidateBuffer(PACSignCtx &verifyCtx, const Byte *jitBuffer, int pos, int size); + PACSignCtx ctx_; + Byte *tmpBuffer_; + int skipSize_; + int offset_; + std::vector signTable_; + std::vector skippedOffset_; + std::queue willSign_; +}; +} +} +} +#endif \ No newline at end of file diff --git a/interfaces/innerkits/jit_code_sign/include/pac_sign_ctx.h b/interfaces/innerkits/jit_code_sign/include/pac_sign_ctx.h new file mode 100644 index 0000000..cad69e5 --- /dev/null +++ b/interfaces/innerkits/jit_code_sign/include/pac_sign_ctx.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CODE_SIGN_PAC_SIGN_CTX_H +#define CODE_SIGN_PAC_SIGN_CTX_H + +#include + +namespace OHOS { +namespace Security { +namespace CodeSign { + +enum CTXPurpose { + Sign, + Verify +}; + +enum ContextType { + SIGN_WITH_CONTEXT, + SIGN_WITHOUT_CONTEXT, + AUTH_CONTEXT +}; + +class PACSignCtx { +public: + PACSignCtx(CTXPurpose purpose = CTXPurpose::Sign, uint32_t salt = 0); + ~PACSignCtx(); + void Init(int index); + uint32_t Update(uint32_t value); + void Finalize(); + uint32_t SignSingle(uint32_t value, uint32_t index); + void SetIndex(uint32_t index); + uint32_t GetSalt(); + +private: + void InitSalt(); + void SetContext(uint32_t context); + uint64_t PaddingContext(ContextType type); + uint64_t GetRealContext(); + uint32_t SignWithContext(uint32_t value); + + uint64_t context_; + uint32_t salt_; + int index_; + CTXPurpose purpose_; +}; +} +} +} +#endif \ No newline at end of file diff --git a/interfaces/innerkits/jit_code_sign/include/random_helper.h b/interfaces/innerkits/jit_code_sign/include/random_helper.h new file mode 100644 index 0000000..a456c33 --- /dev/null +++ b/interfaces/innerkits/jit_code_sign/include/random_helper.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CODE_SIGN_RANDOM_HELPER_H +#define CODE_SIGN_RANDOM_HELPER_H + +#include +#include +#include + #include +#include "errcode.h" + +namespace OHOS { +namespace Security { +namespace CodeSign { + +// Decrease the overhead of opening /dev/urandom +class RandomHelper { +public: + ~RandomHelper() + { + if (fd_ > 0) { + close(fd_); + } + } + + int32_t GetUint32(uint32_t *randomNum) + { + std::lock_guard lock(mutex_); + if (fd_ < 0) { + fd_ = open("/dev/urandom", O_RDONLY); + if (fd_ < 0) { + return CS_ERR_FILE_OPEN; + } + } + uint32_t ret = 0; + ssize_t len = read(fd_, &ret, sizeof(ret)); + if (len != sizeof(ret)) { + return CS_ERR_FILE_READ; + } + *randomNum = ret; + ref_ += 1; + return CS_SUCCESS; + } + + void DeRef() + { + std::lock_guard lock(mutex_); + ref_ -= 1; + if (ref_ == 0) { + (void) close(fd_); + fd_ = -1; + } + } + +private: + int fd_ = -1; + int ref_ = 0; + std::mutex mutex_; +}; +} +} +} +#endif \ No newline at end of file diff --git a/interfaces/innerkits/jit_code_sign/src/jit_code_signer.cpp b/interfaces/innerkits/jit_code_sign/src/jit_code_signer.cpp new file mode 100644 index 0000000..bcbf13a --- /dev/null +++ b/interfaces/innerkits/jit_code_sign/src/jit_code_signer.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jit_code_signer.h" + +#include "errcode.h" +#include "log.h" + +namespace OHOS { +namespace Security { +namespace CodeSign { + +constexpr int32_t INSTRUCTION_SIZE = 4; +constexpr int32_t LOG_2_INSTRUCTION_SIZE = 2; +constexpr int32_t BYTE_BIT_SIZE = 8; + +inline static Instr GetOneInstrForQueue(std::queue &queue) +{ + Instr insn = 0; + int i = 0; + while (i < INSTRUCTION_SIZE && !queue.empty()) { + insn = (insn << BYTE_BIT_SIZE) | queue.front(); + queue.pop(); + i++; + } + return insn; +} + +inline static int GetIndexFromOffset(int offset) +{ + return offset >> LOG_2_INSTRUCTION_SIZE; +} + +JitCodeSigner::JitCodeSigner() +{ + ctx_.Init(0); +} + +void JitCodeSigner::Reset() +{ + ctx_.Init(0); + skipSize_ = 0; + signTable_.clear(); + skippedOffset_.clear(); + while(!willSign_.empty()) { + willSign_.pop(); + } +} + +void JitCodeSigner::RegisterTmpBuffer(Byte *tmpBuffer) +{ + tmpBuffer_ = tmpBuffer; +} + +void JitCodeSigner::SignInstruction(Instr insn) +{ + LOG_INFO(LABEL, "offset = %{public}x, insn = %{public}x", offset_, insn); + if (skipSize_) { + skippedOffset_.push_back(offset_); + signTable_.push_back(ctx_.SignSingle(insn, GetIndexFromOffset(offset_))); + skipSize_ -= 1; + } else { + uint32_t signature = ctx_.Update(insn); + LOG_INFO(LABEL, "signature = %{public}x, size = %{public}u", signature, signTable_.size()); + signTable_.push_back(signature); + } + offset_ += INSTRUCTION_SIZE; +} + +void JitCodeSigner::SignData(const Byte *const data, uint32_t size) +{ + LOG_INFO(LABEL, "size = %{public}d", size); + uint32_t cur = 0; + int unsignedSize = willSign_.size(); + if (unsignedSize == 0 && size >= INSTRUCTION_SIZE) { + auto insnPtr = reinterpret_cast(data); + while (cur + INSTRUCTION_SIZE <= size) { + SignInstruction(*insnPtr); + insnPtr++; + cur += INSTRUCTION_SIZE; + } + } + LOG_INFO(LABEL, "cur = %{public}d, unsign = %{public}d", cur, unsignedSize); + if (cur == size) { + return; + } + + unsignedSize += size - cur; + while (cur < size) { + willSign_.push(*(data + cur)); + cur++; + } + + while (unsignedSize >= INSTRUCTION_SIZE) { + SignInstruction(GetOneInstrForQueue(willSign_)); + unsignedSize -= INSTRUCTION_SIZE; + } +} + +void JitCodeSigner::SkipNext(int n) +{ + skipSize_ = std::max(skipSize_, n); + int startIndex = GetIndexFromOffset(offset_) + skipSize_; + ctx_.Init(startIndex); +} + +int32_t JitCodeSigner::PatchInstruction(int offset, Instr insn) +{ +// #ifdef JIT_CODE_SIGN_DEBUGGABLE + if (std::find(skippedOffset_.begin(), skippedOffset_.end(), offset) == skippedOffset_.end()) { + LOG_ERROR(LABEL, "Update no skipped instruction failed at offset = %{public}x", offset); + return CS_ERR_PATCH_INVALID; + } +// #endif + LOG_INFO(LABEL, "offset = %{public}x, insn = %{public}x", offset, insn); + int curIndex = GetIndexFromOffset(offset); + int signature = ctx_.SignSingle(insn, curIndex); + LOG_INFO(LABEL, "signature = %{public}x", signature); + signTable_[curIndex] = signature; + return CS_SUCCESS; +} + +int32_t JitCodeSigner::PatchInstruction(Byte *buffer, Instr insn) +{ + return PatchInstruction(buffer - tmpBuffer_, insn); +} + +int32_t JitCodeSigner::PatchData(int offset, const Byte *const data, uint32_t size) +{ + if (size & 0x3) { + return CS_ERR_JIT_SIGN_SIZE; + } + auto insnPtr = reinterpret_cast(data); + int ret = 0; + for (uint32_t i = 0; i < size; i += INSTRUCTION_SIZE) { + ret = PatchInstruction(offset + i, *insnPtr); + if (ret != CS_SUCCESS) { + return ret; + } + insnPtr += 1; + } + return CS_SUCCESS; +} + +int32_t JitCodeSigner::AfterPatchingInstructions(int offset, uint32_t n) +{ + if (tmpBuffer_ == nullptr) { + return CS_ERR_TMP_BUFFER; + } + return PatchData(offset, tmpBuffer_ + offset, n * INSTRUCTION_SIZE); +} + +int32_t JitCodeSigner::AfterPatchingInstructions(Byte *buffer, uint32_t n) +{ + if (tmpBuffer_ == nullptr) { + return CS_ERR_TMP_BUFFER; + } + return PatchData(buffer - tmpBuffer_, buffer, n * INSTRUCTION_SIZE); +} + +int32_t JitCodeSigner::ValidateBuffer(PACSignCtx &verifyCtx, const Byte *jitBuffer, int pos, int size) +{ + if (size == 0) { + return CS_SUCCESS; + } + LOG_INFO(LABEL, "validate for offset = %{public}x, size = %{public}d", pos, size); + int32_t index = GetIndexFromOffset(pos); + verifyCtx.Init(index); + auto insnPtr = reinterpret_cast(jitBuffer + pos); + while (size > 0) { + uint32_t signature = verifyCtx.Update(*insnPtr); + if (signature != signTable_[index]) { + LOG_ERROR(LABEL, "validate failed at index = %{public}x, signature(%{public}x) != wanted(%{pucblic}x)", + index, signature, signTable_[index]); + return CS_ERR_VALIDATE_CODE; + } + index++; + insnPtr++; + size -= INSTRUCTION_SIZE; + } + return CS_SUCCESS; +} + +int32_t JitCodeSigner::ValidateCodeCopy(const Byte *tmpBuffer, int size) +{ + if (tmpBuffer == nullptr) { + tmpBuffer = tmpBuffer_; + } else if (tmpBuffer_ && tmpBuffer != tmpBuffer_) { + return CS_ERR_TMP_BUFFER; + } + + if (size & 0x3 || size > signTable_.size() * INSTRUCTION_SIZE) { + return CS_ERR_JIT_SIGN_SIZE; + } + + PACSignCtx verifyCtx(CTXPurpose::Verify, ctx_.GetSalt()); + int offset = 0; + for (uint32_t i = 0; i < skippedOffset_.size(); i++) { + if (ValidateBuffer(verifyCtx, tmpBuffer, offset, skippedOffset_[i] - offset) != CS_SUCCESS) { + return CS_ERR_VALIDATE_CODE; + } + + int32_t index = GetIndexFromOffset(skippedOffset_[i]); + Instr insn = *reinterpret_cast(tmpBuffer + skippedOffset_[i]); + uint32_t signature = verifyCtx.SignSingle(insn, index); + if (signature != signTable_[index]) { + LOG_ERROR(LABEL, "validate insn(%{public}x) without context failed at index = %{public}x," \ + "signature(%{public}x) != wanted(%{pucblic}x)", + insn, index, signature, signTable_[index]); + return CS_ERR_VALIDATE_CODE; + } + offset = skippedOffset_[i] + INSTRUCTION_SIZE; + } + + if (ValidateBuffer(verifyCtx, tmpBuffer, offset, size - offset) != CS_SUCCESS) { + return CS_ERR_VALIDATE_CODE; + } + return CS_SUCCESS; +} +} +} +} \ No newline at end of file diff --git a/interfaces/innerkits/jit_code_sign/src/pac_sign_ctx.cpp b/interfaces/innerkits/jit_code_sign/src/pac_sign_ctx.cpp new file mode 100644 index 0000000..bfacd6f --- /dev/null +++ b/interfaces/innerkits/jit_code_sign/src/pac_sign_ctx.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "pac_sign_ctx.h" + +#include +#include "log.h" +#include "random_helper.h" + +namespace OHOS { +namespace Security { +namespace CodeSign { + +constexpr uint64_t SIGN_WITH_CONTEXT_PREFIX = 0x2LL << 60; +constexpr uint64_t SIGN_WITHOUT_CONTEXT_PREFIX = 0x3LL << 60; +constexpr uint64_t AUTH_CONTEXT_PREFIX = 0x1LL << 60; + +static RandomHelper g_saltHelper; + +static inline uint64_t PACDB(uint64_t value, uint64_t modifier) +{ +#ifdef SUPPORT_PAC + asm volatile("pacdb %0, %1" : "+r"(value) : "r"(modifier) :); + return value; +#else + LOG_INFO(LABEL, "value = %{public}llx, modifier = %{public}llx, ret = %{public}llx", + value, modifier, value ^ modifier); + return value ^ modifier; +#endif +} + +static inline uint64_t AUTDB(uint64_t value, uint64_t modifier) +{ +#ifdef SUPPORT_PAC + asm volatile("autdb %0, %1" : "+r"(value) : "r"(modifier) :); + return value; +#else + LOG_INFO(LABEL, "value = %{public}llx, modifier = %{public}llx, ret = %{public}llx", + value, modifier, value ^ modifier); + return value ^ modifier; +#endif +} + + +static inline uint32_t PACGA(uint64_t value, uint64_t modifier) +{ + uint64_t ret = 0; +#ifdef SUPPORT_PAC + asm volatile("pacga %0, %1, %2" : "=r"(ret) : "r"(value) , "r"(modifier) :); + return static_cast(ret >> 32); +#else + ret = value ^ modifier; + LOG_INFO(LABEL, "value = %{public}llx, modifier = %{public}llx, ret = %{public}llx", + value, modifier, ret); + return static_cast(ret); +#endif +} + +PACSignCtx::PACSignCtx(CTXPurpose purpose, uint32_t salt): + context_(0), salt_(salt), index_(0), purpose_(purpose) +{ + LOG_INFO(LABEL, "salt_ = %{public}x", salt_); + if (purpose_ == CTXPurpose::Sign) { + InitSalt(); + } +} + +PACSignCtx::~PACSignCtx() +{ + // if salt_ == 0, generating salt failed + if (purpose_ == CTXPurpose::Sign && salt_) { + g_saltHelper.DeRef(); + } +} + +void PACSignCtx::InitSalt() +{ + // generate salt using /dev/urandom + if (g_saltHelper.GetUint32(&salt_) != CS_SUCCESS) { + LOG_ERROR(LABEL, "init salt faild."); + } +} + +void PACSignCtx::Init(int index) +{ + index_ = index; + SetContext(GetSalt()); +} + +uint64_t PACSignCtx::PaddingContext(ContextType type) +{ + uint32_t context; + uint64_t prefix; + switch (type) { + case SIGN_WITH_CONTEXT: + context = context_; + prefix = SIGN_WITH_CONTEXT_PREFIX; + break; + case SIGN_WITHOUT_CONTEXT: + context = GetSalt(); + prefix = SIGN_WITHOUT_CONTEXT_PREFIX; + break; + case AUTH_CONTEXT: + context = GetSalt(); + prefix = AUTH_CONTEXT_PREFIX; + } + uint64_t ret = prefix | ((static_cast(index_) & 0xfffffff) << 32) | context; + LOG_INFO(LABEL, "context = %{public}llx", ret); + return ret; +} + +uint64_t PACSignCtx::GetRealContext() +{ + uint64_t paddingContext = PaddingContext(AUTH_CONTEXT); + return AUTDB(context_, paddingContext); +} + +uint32_t PACSignCtx::SignWithContext(uint32_t value) +{ + uint64_t paddingContext = PaddingContext(SIGN_WITH_CONTEXT); + return PACGA(value, paddingContext); +} + +void PACSignCtx::SetContext(uint32_t context) +{ + if (purpose_ == CTXPurpose::Verify) { + context_ = context; + return; + } + uint64_t paddingContext = PaddingContext(AUTH_CONTEXT); + LOG_INFO(LABEL, "Before encrypt context_ = %{public}x", context); + context_ = PACDB(context, paddingContext); + LOG_INFO(LABEL, "After encrypt context_ = %{public}llx", context_); +} + +uint32_t PACSignCtx::Update(uint32_t value) +{ + if (purpose_ == CTXPurpose::Sign) { + context_ = GetRealContext(); + LOG_INFO(LABEL, "real comtext_ = %{public}llx", context_); + } + index_ += 1; + uint32_t signature = SignWithContext(value); + SetContext(signature); + return signature; +} + +void PACSignCtx::Finalize() +{ + if (purpose_ == CTXPurpose::Sign) { + (void) GetRealContext(); + } +} + +uint32_t PACSignCtx::SignSingle(uint32_t value, uint32_t index) +{ + index_ = index; + uint64_t paddingContext = PaddingContext(SIGN_WITHOUT_CONTEXT); + return PACGA(value, paddingContext); +} + +uint32_t PACSignCtx::GetSalt() +{ + return salt_; +} +} +} +} \ No newline at end of file diff --git a/interfaces/innerkits/jit_code_sign/src/test_src.cpp b/interfaces/innerkits/jit_code_sign/src/test_src.cpp new file mode 100644 index 0000000..4b9b502 --- /dev/null +++ b/interfaces/innerkits/jit_code_sign/src/test_src.cpp @@ -0,0 +1,47 @@ +#include + +#include "pac_sign_ctx.h" + +using namespace OHOS::Security::CodeSign; + + +int main() +{ + std::cout << "sign" << std::endl; + PACSignCtx ctx; + uint32_t signature; + ctx.Init(0); + signature = ctx.Update(0x11111111); + std::cout << std::hex << signature << std::endl; + signature = ctx.Update(0x22222222); + std::cout << std::hex << signature << std::endl; + signature = ctx.Update(0x33333333); + std::cout << std::hex << signature << std::endl; + ctx.Finalize(); + + signature = ctx.SignSingle(0x44444444, 3); + std::cout << std::hex << signature << std::endl; + + ctx.Init(4); + signature = ctx.Update(0x55555555); + std::cout << std::hex << signature << std::endl; + + std::cout << "verify" << std::endl; + PACSignCtx ctx2(CTXPurpose::Verify, ctx.GetSalt()); + ctx2.Init(0); + signature = ctx2.Update(0x11111111); + std::cout << std::hex << signature << std::endl; + signature = ctx2.Update(0x22222222); + std::cout << std::hex << signature << std::endl; + signature = ctx2.Update(0x33333333); + std::cout << std::hex << signature << std::endl; + ctx2.Finalize(); + + signature = ctx2.SignSingle(0x44444444, 3); + std::cout << std::hex << signature << std::endl; + + ctx2.Init(4); + signature = ctx2.Update(0x55555555); + std::cout << std::hex << signature << std::endl; + return 0; +} \ No newline at end of file diff --git a/test/unittest/BUILD.gn b/test/unittest/BUILD.gn index 9acd9b5..082fbfa 100644 --- a/test/unittest/BUILD.gn +++ b/test/unittest/BUILD.gn @@ -214,6 +214,22 @@ ohos_unittest("enable_verity_ioctl_unittest") { ] } +ohos_unittest("jit_code_sign_unittest") { + module_out_path = "security/code_signature" + include_dirs = [ + "${code_signature_root_dir}/interfaces/innerkits/jit_code_sign/include", + ] + sources = [ "jit_code_sign_test.cpp" ] + deps = [ + "${code_signature_root_dir}/interfaces/innerkits/jit_code_sign:libjit_code_sign" + ] + external_deps = [ + "c_utils:utils", + ] + subsystem_name = "security" + part_name = "code_signature" +} + group("unittest_group") { testonly = true if (!defined(ohos_lite)) { @@ -225,6 +241,7 @@ group("unittest_group") { ":multi_thread_local_sign_unittest", ":rust_key_enable_unittest", ":sign_and_enforce_unittest", + ":jit_code_sign_unittest" ] } } diff --git a/test/unittest/jit_code_sign_test.cpp b/test/unittest/jit_code_sign_test.cpp new file mode 100644 index 0000000..0cdbc2f --- /dev/null +++ b/test/unittest/jit_code_sign_test.cpp @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "errcode.h" +#include "jit_buffer_integrity.h" +#include "pac_sign_ctx.h" + +namespace OHOS { +namespace Security { +namespace CodeSign { +using namespace std; +using namespace testing::ext; + +#define CAST_VOID(buffer) reinterpret_cast(buffer) + +static Instr g_testInstructionSet[] = { + 0x11111111, + 0x22222222, + 0x33333333, // patched -> 0x66666666 + 0x44444444, // patched -> 0x77777777 + 0x55555555 +}; + +static Instr g_AfterPatchInstructionSet[] = { + 0x11111111, + 0x22222222, + 0x66666666, + 0x77777777, + 0x55555555 +}; + +static Instr g_testPatchInstructionSet[] = { + 0x66666666, + 0x77777777 +}; + +static constexpr int INSTRUCTIONS_SET_SIZE = sizeof(g_testInstructionSet) / sizeof(g_testInstructionSet[0]); +static constexpr int INSTRUCTIONS_SET_SIZE_BYTES = sizeof(g_testInstructionSet); +static constexpr int TEST_PATCH_INDEX = 2; + +static constexpr int PATCH_INSTRUCTIONS_SET_SIZE = sizeof(g_testPatchInstructionSet) / sizeof(g_testPatchInstructionSet[0]); +constexpr int32_t INSTRUCTION_SIZE = sizeof(Instr); + +static void *g_testInstructionBuf = CAST_VOID(g_testInstructionSet); +static void *g_afterPatchInstructionBuf = CAST_VOID(g_AfterPatchInstructionSet); +static void *g_testPatchInstructionBuf = CAST_VOID(g_testPatchInstructionSet); +static void *g_jitMemory = nullptr; + +static inline void AllocJitMemory() +{ + g_jitMemory = mmap(nullptr, INSTRUCTIONS_SET_SIZE_BYTES, + PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, 0, 0); + EXPECT_NE(g_jitMemory, MAP_FAILED); +} + +static inline void FreeJitMemory() +{ + munmap(g_jitMemory, INSTRUCTIONS_SET_SIZE_BYTES); +} + +class JitCodeSignTest : public testing::Test { +public: + JitCodeSignTest() {}; + virtual ~JitCodeSignTest() {}; + + static void SetUpTestCase() + { + AllocJitMemory(); + }; + + static void TearDownTestCase() + { + FreeJitMemory(); + }; + + void SetUp() {}; + void TearDown() {}; +}; + +/** + * @tc.name: JitCodeSignTest_0001 + * @tc.desc: sign instructions and verify succuss + * @tc.type: Func + * @tc.require: + */ +HWTEST_F(JitCodeSignTest, JitCodeSignTest_0001, TestSize.Level0) +{ + // PACSignCtx signCtx; + // std::queue signature; + // while (i < TEST_PATCH_INDEX) { + // signature.push(signCtx.Update(g_testInstructionSet[i])) + // } + // for (int i = 0; i < 2; i++) { + // signature.push(); + // } + // signature.push(signCtx.SignSingle()); + // for (auto insn : g_testInstructionSet2) { + // signature.push(signCtx.Update(insn)); + // } + + // PACSignCtx verifyCtx(CTXPurpose::Verify, signCtx.GetSalt()); + // for (auto insn : g_testInstructionSet1) { + // EXPECT_EQ(verifyCtx.Update(insn), signature.front()); + // signature.pop(); + // } + // EXPECT_EQ(verifyCtx.SignSingle(), signature.front()); + // signature.pop(); + // for (auto insn : g_testInstructionSet2) { + // EXPECT_EQ(verifyCtx.Update(insn), signature.front()); + // signature.pop(); + // } + + JitCodeSigner signer; + int i = 0; + while (i < INSTRUCTIONS_SET_SIZE) { + AppendInstruction(&signer, g_testInstructionSet[i]); + i++; + } + + EXPECT_EQ(CopyToJitCache(&signer, g_jitMemory, g_testInstructionSet, INSTRUCTIONS_SET_SIZE_BYTES), CS_SUCCESS); + EXPECT_EQ(memcmp(g_jitMemory, g_testInstructionSet, INSTRUCTIONS_SET_SIZE_BYTES), 0); +} + + +/** + * @tc.name: JitCodeSignTest_0002 + * @tc.desc: sign data and verify succuss + * @tc.type: Func + * @tc.require: + */ +HWTEST_F(JitCodeSignTest, JitCodeSignTest_0002, TestSize.Level0) +{ + JitCodeSigner signer; + AppendData(&signer, g_testInstructionBuf, INSTRUCTIONS_SET_SIZE_BYTES); + + EXPECT_EQ(CopyToJitCache(&signer, g_jitMemory, g_testInstructionBuf, INSTRUCTIONS_SET_SIZE_BYTES), CS_SUCCESS); + EXPECT_EQ(memcmp(g_jitMemory, g_testInstructionBuf, INSTRUCTIONS_SET_SIZE_BYTES), 0); +} + +/** + * @tc.name: JitCodeSignTest_0003 + * @tc.desc: sign and patch instructions succuss + * @tc.type: Func + * @tc.require: + */ +HWTEST_F(JitCodeSignTest, JitCodeSignTest_0003, TestSize.Level0) +{ + JitCodeSigner signer; + int i = 0, offset = 0; + while (i < TEST_PATCH_INDEX) { + AppendInstruction(&signer, g_testInstructionSet[i]); + i++; + } + for (int j = 0; j < PATCH_INSTRUCTIONS_SET_SIZE; j++) { + WillFixUp(&signer, 1); + AppendInstruction(&signer, g_testInstructionSet[i]); + i++; + } + while (i < INSTRUCTIONS_SET_SIZE) { + AppendInstruction(&signer, g_testInstructionSet[i]); + i++; + } + offset = TEST_PATCH_INDEX * INSTRUCTION_SIZE; + for (int j = 0; j < PATCH_INSTRUCTIONS_SET_SIZE; j++) { + PatchInstruction(&signer, offset, g_testPatchInstructionSet[j]); + offset += INSTRUCTION_SIZE; + } + + EXPECT_EQ(CopyToJitCache(&signer, g_jitMemory, g_afterPatchInstructionBuf, INSTRUCTIONS_SET_SIZE_BYTES), CS_SUCCESS); + EXPECT_EQ(memcmp(g_jitMemory, g_afterPatchInstructionBuf, INSTRUCTIONS_SET_SIZE_BYTES), 0); +} + +/** + * @tc.name: JitCodeSignTest_0004 + * @tc.desc: sign and patch data succuss + * @tc.type: Func + * @tc.require: + */ +HWTEST_F(JitCodeSignTest, JitCodeSignTest_0004, TestSize.Level0) +{ + JitCodeSigner signer; + int i = 0, offset = 0; + while (i < TEST_PATCH_INDEX) { + AppendInstruction(&signer, g_testInstructionSet[i]); + offset += INSTRUCTION_SIZE; + i++; + } + + int patchSize = sizeof(g_testPatchInstructionSet); + WillFixUp(&signer, PATCH_INSTRUCTIONS_SET_SIZE); + AppendData(&signer, CAST_VOID(&g_testInstructionSet[i]), patchSize); + i += PATCH_INSTRUCTIONS_SET_SIZE; + offset += patchSize; + + while (i < INSTRUCTIONS_SET_SIZE) { + AppendInstruction(&signer, g_testInstructionSet[i]); + i++; + } + + offset = TEST_PATCH_INDEX * INSTRUCTION_SIZE; + PatchData(&signer, offset, g_testPatchInstructionBuf, patchSize); + + EXPECT_EQ(CopyToJitCache(&signer, g_jitMemory, g_afterPatchInstructionBuf, INSTRUCTIONS_SET_SIZE_BYTES), CS_SUCCESS); + EXPECT_EQ(memcmp(g_jitMemory, g_afterPatchInstructionBuf, INSTRUCTIONS_SET_SIZE_BYTES), 0); +} + +/** + * @tc.name: JitCodeSignTest_0005 + * @tc.desc: sign and copy wrong data failed + * @tc.type: Func + * @tc.require: + */ +HWTEST_F(JitCodeSignTest, JitCodeSignTest_0005, TestSize.Level0) +{ + JitCodeSigner signer; + AppendData(&signer, g_testInstructionBuf, INSTRUCTIONS_SET_SIZE_BYTES); + int sizeInByte = sizeof(g_testInstructionSet); + EXPECT_EQ(CopyToJitCache(&signer, g_jitMemory, g_afterPatchInstructionBuf, sizeInByte), CS_ERR_VALIDATE_CODE); +} + +/** + * @tc.name: JitCodeSignTest_0006 + * @tc.desc: sign and copy with wrong size failed + * @tc.type: Func + * @tc.require: + */ +HWTEST_F(JitCodeSignTest, JitCodeSignTest_0006, TestSize.Level0) +{ + JitCodeSigner signer; + RegisterTmpBuffer(&signer, g_testInstructionBuf); + AppendData(&signer, g_testInstructionBuf, INSTRUCTIONS_SET_SIZE_BYTES); + + EXPECT_EQ(CopyToJitCache(&signer, g_jitMemory, g_testInstructionBuf, INSTRUCTIONS_SET_SIZE_BYTES - 1), CS_ERR_JIT_SIGN_SIZE); +} + +/** + * @tc.name: JitCodeSignTest_0007 + * @tc.desc: sign and copy with buffer failed + * @tc.type: Func + * @tc.require: + */ +HWTEST_F(JitCodeSignTest, JitCodeSignTest_0007, TestSize.Level0) +{ + JitCodeSigner signer; + RegisterTmpBuffer(&signer, g_testInstructionBuf); + AppendData(&signer, g_testInstructionBuf, INSTRUCTIONS_SET_SIZE_BYTES - 1); + + EXPECT_EQ(CopyToJitCache(&signer, g_jitMemory, g_afterPatchInstructionBuf, INSTRUCTIONS_SET_SIZE_BYTES), CS_ERR_TMP_BUFFER); +} + +/** + * @tc.name: JitCodeSignTest_0008 + * @tc.desc: sign data without 4 byte-alignment and copy success + * @tc.type: Func + * @tc.require: + */ +HWTEST_F(JitCodeSignTest, JitCodeSignTest_0008, TestSize.Level0) +{ + JitCodeSigner signer; + Byte *ptr = reinterpret_cast(g_testInstructionBuf) + 1; + AppendData(&signer, g_testInstructionBuf, 1); + AppendData(&signer, CAST_VOID(ptr), INSTRUCTIONS_SET_SIZE_BYTES - 1); + + EXPECT_EQ(CopyToJitCache(&signer, g_jitMemory, g_testInstructionBuf, INSTRUCTIONS_SET_SIZE_BYTES), CS_SUCCESS); +} + +/** + * @tc.name: JitCodeSignTest_0009 + * @tc.desc: sign data and patch without 4 byte-alignment failed + * @tc.type: Func + * @tc.require: + */ +HWTEST_F(JitCodeSignTest, JitCodeSignTest_0009, TestSize.Level0) +{ + JitCodeSigner signer; + int i = 0, offset = 0; + while (i < TEST_PATCH_INDEX) { + AppendInstruction(&signer, g_testInstructionSet[i]); + offset += INSTRUCTION_SIZE; + i++; + } + + int patchSize = sizeof(g_testPatchInstructionSet); + WillFixUp(&signer, PATCH_INSTRUCTIONS_SET_SIZE); + AppendData(&signer, CAST_VOID(&g_testInstructionSet[i]), patchSize); + i += PATCH_INSTRUCTIONS_SET_SIZE; + offset += patchSize; + + while (i < INSTRUCTIONS_SET_SIZE) { + AppendInstruction(&signer, g_testInstructionSet[i]); + i++; + } + + offset = TEST_PATCH_INDEX * INSTRUCTION_SIZE; + EXPECT_EQ(PatchData(&signer, offset, g_testPatchInstructionBuf, patchSize - 1), CS_ERR_JIT_SIGN_SIZE); +} + +/** + * @tc.name: JitCodeSignTest_00010 + * @tc.desc: sign data and patch with patched buffer successfully + * @tc.type: Func + * @tc.require: + */ +HWTEST_F(JitCodeSignTest, JitCodeSignTest_00010, TestSize.Level0) +{ + JitCodeSigner signer; + RegisterTmpBuffer(&signer, g_afterPatchInstructionBuf); + int i = 0, offset = 0; + while (i < TEST_PATCH_INDEX) { + AppendInstruction(&signer, g_testInstructionSet[i]); + offset += INSTRUCTION_SIZE; + i++; + } + + int patchSize = sizeof(g_testPatchInstructionSet); + WillFixUp(&signer, PATCH_INSTRUCTIONS_SET_SIZE); + AppendData(&signer, CAST_VOID(&g_testInstructionSet[i]), patchSize); + i += PATCH_INSTRUCTIONS_SET_SIZE; + offset += patchSize; + + while (i < INSTRUCTIONS_SET_SIZE) { + AppendInstruction(&signer, g_testInstructionSet[i]); + i++; + } + + offset = TEST_PATCH_INDEX * INSTRUCTION_SIZE; + signer.AfterPatchingInstructions(offset, PATCH_INSTRUCTIONS_SET_SIZE); + EXPECT_EQ(CopyToJitCache(&signer, g_jitMemory, g_afterPatchInstructionBuf, INSTRUCTIONS_SET_SIZE_BYTES), CS_SUCCESS); + EXPECT_EQ(memcmp(g_jitMemory, g_afterPatchInstructionBuf, INSTRUCTIONS_SET_SIZE_BYTES), 0); +} + +/** + * @tc.name: JitCodeSignTest_00011 + * @tc.desc: sign data and patch without buffer failed + * @tc.type: Func + * @tc.require: + */ +HWTEST_F(JitCodeSignTest, JitCodeSignTest_00011, TestSize.Level0) +{ + JitCodeSigner signer; + int i = 0, offset = 0; + while (i < TEST_PATCH_INDEX) { + AppendInstruction(&signer, g_testInstructionSet[i]); + offset += INSTRUCTION_SIZE; + i++; + } + + int patchSize = sizeof(g_testPatchInstructionSet); + WillFixUp(&signer, PATCH_INSTRUCTIONS_SET_SIZE); + AppendData(&signer, CAST_VOID(&g_testInstructionSet[i]), patchSize); + i += PATCH_INSTRUCTIONS_SET_SIZE; + offset += patchSize; + + while (i < INSTRUCTIONS_SET_SIZE) { + AppendInstruction(&signer, g_testInstructionSet[i]); + i++; + } + + offset = TEST_PATCH_INDEX * INSTRUCTION_SIZE; + EXPECT_EQ(signer.AfterPatchingInstructions(offset, PATCH_INSTRUCTIONS_SET_SIZE), CS_ERR_TMP_BUFFER); +} + +/** + * @tc.name: JitCodeSignTest_00012 + * @tc.desc: reset jit memory success + * @tc.type: Func + * @tc.require: + */ +HWTEST_F(JitCodeSignTest, JitCodeSignTest_00012, TestSize.Level0) +{ + Byte tmpBuffer[INSTRUCTIONS_SET_SIZE_BYTES] = {0}; + ResetJitCacheOnly(g_jitMemory, INSTRUCTIONS_SET_SIZE_BYTES); + EXPECT_EQ(memcmp(g_jitMemory, tmpBuffer, INSTRUCTIONS_SET_SIZE_BYTES), 0); +} + +/** + * @tc.name: JitCodeSignTest_00013 + * @tc.desc: copy failed with wrong size + * @tc.type: Func + * @tc.require: + */ +HWTEST_F(JitCodeSignTest, JitCodeSignTest_00013, TestSize.Level0) +{ + JitCodeSigner signer; + AppendData(&signer, g_testInstructionBuf, INSTRUCTIONS_SET_SIZE_BYTES); + EXPECT_EQ(CopyToJitCache(&signer, g_jitMemory, g_testInstructionBuf, INSTRUCTIONS_SET_SIZE_BYTES - 1), CS_ERR_JIT_SIGN_SIZE); + + signer.Reset(); + EXPECT_EQ(CopyToJitCache(&signer, g_jitMemory, g_testInstructionBuf, INTRUSCTIONS_SET_SIZE_BYTES), CS_ERR_JIT_SIGN_SIZE); +} +} +} +} \ No newline at end of file -- Gitee