1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "jit_code_signer_hybrid.h"
17 
18 #include <sstream>
19 #include "errcode.h"
20 #include "log.h"
21 
22 namespace OHOS {
23 namespace Security {
24 namespace CodeSign {
JitCodeSignerHybrid()25 JitCodeSignerHybrid::JitCodeSignerHybrid()
26 {
27     Reset();
28 }
29 
Reset()30 void JitCodeSignerHybrid::Reset()
31 {
32     tmpBuffer_ = nullptr;
33     ctx_.InitSalt();
34     ctx_.Init(0);
35     ctxInited_ = true;
36     skipSize_ = 0;
37     offset_ = 0;
38     signTable_.clear();
39     skippedOffset_.clear();
40     while (!willSign_.empty()) {
41         willSign_.pop();
42     }
43 }
44 
SignInstruction(Instr insn)45 void JitCodeSignerHybrid::SignInstruction(Instr insn)
46 {
47 #ifdef JIT_CODE_SIGN_DEBUGGABLE
48     if (static_cast<size_t>(GetIndexFromOffset(offset_)) != signTable_.size()) {
49         LOG_ERROR("Index = %{public}d not equal signtable size = %{public}zu.",
50             GetIndexFromOffset(offset_), signTable_.size());
51     }
52     LOG_INFO("Offset = %{public}x, insn = %{public}x", offset_, insn);
53 #endif
54     if (skipSize_ > 0) {
55         skippedOffset_.push_back(offset_);
56         signTable_.push_back(ctx_.SignSingle(insn, GetIndexFromOffset(offset_)));
57         skipSize_ -= 1;
58     } else {
59         if (!ctxInited_) {
60             ctx_.Init(GetIndexFromOffset(offset_));
61             ctxInited_ = true;
62         }
63         uint32_t signature = ctx_.Update(insn);
64         signTable_.push_back(signature);
65     }
66     offset_ += INSTRUCTION_SIZE;
67 }
68 
SkipNext(uint32_t n)69 void JitCodeSignerHybrid::SkipNext(uint32_t n)
70 {
71     skipSize_ = std::max(skipSize_, n);
72     ctxInited_ = false;
73 }
74 
PatchInstruction(int offset,Instr insn)75 int32_t JitCodeSignerHybrid::PatchInstruction(int offset, Instr insn)
76 {
77 #ifdef JIT_CODE_SIGN_DEBUGGABLE
78     if (std::find(skippedOffset_.begin(), skippedOffset_.end(), offset)
79         == skippedOffset_.end()) {
80         LOG_ERROR("Update no skipped instruction failed at offset" \
81             "= %{public}x", offset);
82     }
83     LOG_INFO("offset = %{public}x, insn = %{public}x", offset, insn);
84 #endif
85     int curIndex = 0;
86     if (!ConvertPatchOffsetToIndex(offset, curIndex)) {
87         LOG_ERROR("Offset invalid");
88         return CS_ERR_PATCH_INVALID;
89     }
90     uint32_t signature = ctx_.SignSingle(insn, curIndex);
91     signTable_[curIndex] = signature;
92     return CS_SUCCESS;
93 }
94 
ValidateSubCode(Instr * jitMemory,PACSignCtx & verifyCtx,Byte * jitBuffer,int pos,int size)95 int32_t JitCodeSignerHybrid::ValidateSubCode(Instr *jitMemory, PACSignCtx &verifyCtx,
96     Byte *jitBuffer, int pos, int size)
97 {
98     if (size == 0) {
99         return CS_SUCCESS;
100     }
101 #if defined(JIT_CODE_SIGN_DEBUGGABLE) && defined(JIT_FORT_DISABLE)
102     LOG_INFO("Validate start = %{public}p, offset = %{public}x, size = %{public}d",
103         jitBuffer, pos, size);
104 #endif
105     int32_t index = GetIndexFromOffset(pos);
106     verifyCtx.Init(index);
107     auto insnPtr = reinterpret_cast<const Instr *>(jitBuffer + pos);
108     while (size > 0) {
109         uint32_t signature = verifyCtx.Update(*insnPtr);
110         if (signature != signTable_[index]) {
111 #ifdef JIT_FORT_DISABLE
112             LOG_ERROR("Validate insn (%{public}8x) failed at offset = %{public}x, " \
113                 "signature(%{public}x) != wanted(%{public}x)",
114                 *(insnPtr), index * INSTRUCTION_SIZE, signature, signTable_[index]);
115 #endif
116 #ifndef JIT_CODE_SIGN_PERMISSIVE
117             return CS_ERR_VALIDATE_CODE;
118 #else
119             break;
120 #endif
121         }
122         *(jitMemory + index) = *insnPtr;
123         index++;
124         insnPtr++;
125         size -= INSTRUCTION_SIZE;
126     }
127     return CS_SUCCESS;
128 }
129 
ValidateCodeCopy(Instr * jitMemory,Byte * tmpBuffer,int size)130 __attribute__((no_sanitize("cfi"))) int32_t JitCodeSignerHybrid::ValidateCodeCopy(
131     Instr *jitMemory, Byte *tmpBuffer, int size)
132 {
133     int32_t ret = CheckDataCopy(jitMemory, tmpBuffer, size);
134     if (ret != CS_SUCCESS) {
135         return ret;
136     }
137 
138     PACSignCtx verifyCtx(CTXPurpose::VERIFY, ctx_.GetSalt());
139     int offset = 0;
140     for (uint32_t i = 0; i < skippedOffset_.size(); i++) {
141         if (ValidateSubCode(jitMemory, verifyCtx, tmpBuffer_, offset,
142             skippedOffset_[i] - offset) != CS_SUCCESS) {
143             return CS_ERR_VALIDATE_CODE;
144         }
145 
146         int32_t index = GetIndexFromOffset(skippedOffset_[i]);
147         Instr insn = *reinterpret_cast<const Instr *>(tmpBuffer_ + skippedOffset_[i]);
148         uint32_t signature = verifyCtx.SignSingle(insn, index);
149         if (signature != signTable_[index]) {
150 #ifdef JIT_FORT_DISABLE
151             LOG_ERROR("Validate insn (%{public}x) without context failed at index = %{public}x," \
152                 "signature(%{public}x) != wanted(%{public}x).",
153                 insn, index, signature, signTable_[index]);
154 #endif
155 #ifndef JIT_CODE_SIGN_PERMISSIVE
156             return CS_ERR_VALIDATE_CODE;
157 #endif
158         }
159         *(jitMemory + index) = insn;
160         offset = skippedOffset_[i] + INSTRUCTION_SIZE;
161     }
162 
163     if (ValidateSubCode(jitMemory, verifyCtx, tmpBuffer_,
164         offset, size - offset) != CS_SUCCESS) {
165         return CS_ERR_VALIDATE_CODE;
166     }
167     return CS_SUCCESS;
168 }
169 }
170 }
171 }
172