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.h"
17
18 #include <sstream>
19 #ifndef JIT_FORT_DISABLE
20 #include "securec.h"
21 #endif
22 #include "errcode.h"
23 #include "log.h"
24
25 namespace OHOS {
26 namespace Security {
27 namespace CodeSign {
28
29 constexpr int32_t BYTE_BIT_SIZE = 8;
30 constexpr uint32_t UNALIGNMENT_MASK = 0x3;
31
JitCodeSigner()32 JitCodeSigner::JitCodeSigner()
33 {
34 Reset();
35 }
36
Reset()37 void JitCodeSigner::Reset()
38 {
39 tmpBuffer_ = nullptr;
40 ctx_.InitSalt();
41 ctx_.Init(0);
42 signTable_.clear();
43 offset_ = 0;
44 }
45
GetOneInstrForQueue(std::queue<Byte> & queue)46 inline static Instr GetOneInstrForQueue(std::queue<Byte> &queue)
47 {
48 Instr insn = 0;
49 int i = 0;
50 while ((i < INSTRUCTION_SIZE) && !queue.empty()) {
51 insn |= (queue.front() << (BYTE_BIT_SIZE * i));
52 queue.pop();
53 i++;
54 }
55 return insn;
56 }
57
RegisterTmpBuffer(Byte * tmpBuffer)58 void JitCodeSigner::RegisterTmpBuffer(Byte *tmpBuffer)
59 {
60 tmpBuffer_ = tmpBuffer;
61 }
62
SignData(const Byte * const data,uint32_t size)63 int32_t JitCodeSigner::SignData(const Byte *const data, uint32_t size)
64 {
65 if (data == nullptr) {
66 return CS_ERR_INVALID_DATA;
67 }
68 uint32_t cur = 0;
69 size_t unsignedSize = willSign_.size();
70 if ((unsignedSize == 0) && (size >= INSTRUCTION_SIZE)) {
71 auto insnPtr = reinterpret_cast<const Instr *const>(data);
72 while (cur + INSTRUCTION_SIZE <= size) {
73 SignInstruction(*insnPtr);
74 insnPtr++;
75 cur += INSTRUCTION_SIZE;
76 }
77 }
78
79 if (cur == size) {
80 return CS_SUCCESS;
81 }
82 unsignedSize += size - cur;
83 while (cur < size) {
84 willSign_.push(*(data + cur));
85 cur++;
86 }
87
88 while (unsignedSize >= INSTRUCTION_SIZE) {
89 Instr insn = GetOneInstrForQueue(willSign_);
90 SignInstruction(insn);
91 unsignedSize -= INSTRUCTION_SIZE;
92 }
93 return CS_SUCCESS;
94 }
95
PatchInstruction(Byte * buffer,Instr insn)96 int32_t JitCodeSigner::PatchInstruction(Byte *buffer, Instr insn)
97 {
98 if ((buffer == nullptr) || (tmpBuffer_ == nullptr)) {
99 return CS_ERR_PATCH_INVALID;
100 }
101 return PatchInstruction(static_cast<int>(buffer - tmpBuffer_), insn);
102 }
103
PatchData(int offset,const Byte * const data,uint32_t size)104 int32_t JitCodeSigner::PatchData(int offset, const Byte *const data, uint32_t size)
105 {
106 if (size & UNALIGNMENT_MASK) {
107 return CS_ERR_JIT_SIGN_SIZE;
108 }
109 if (data == nullptr) {
110 return CS_ERR_INVALID_DATA;
111 }
112 auto insnPtr = reinterpret_cast<const Instr *const>(data);
113 for (uint32_t i = 0; i < size; i += INSTRUCTION_SIZE) {
114 int ret = PatchInstruction(offset + i, *insnPtr);
115 if (ret != CS_SUCCESS) {
116 return ret;
117 }
118 insnPtr += 1;
119 }
120 return CS_SUCCESS;
121 }
122
PatchData(Byte * buffer,const Byte * const data,uint32_t size)123 int32_t JitCodeSigner::PatchData(Byte *buffer, const Byte *const data, uint32_t size)
124 {
125 if ((buffer == nullptr) || (tmpBuffer_ == nullptr)) {
126 return CS_ERR_PATCH_INVALID;
127 }
128 return PatchData(static_cast<int>(buffer - tmpBuffer_), data, size);
129 }
130
131 #ifndef JIT_FORT_DISABLE
FlushLog()132 void JitCodeSigner::FlushLog()
133 {
134 for (auto &log: deferredLogs) {
135 LOG_ERROR("%{public}s", log.message);
136 }
137 deferredLogs.clear();
138 // There's at most 1 log, for now. No need to shrink.
139 }
140 #endif
141
ConvertPatchOffsetToIndex(const int offset,int & curIndex)142 bool JitCodeSigner::ConvertPatchOffsetToIndex(const int offset, int &curIndex)
143 {
144 if ((offset < 0) || ((static_cast<uint32_t>(offset) & UNALIGNMENT_MASK) != 0)) {
145 return false;
146 }
147 curIndex = GetIndexFromOffset(offset);
148 if (static_cast<size_t>(curIndex) >= signTable_.size()) {
149 LOG_ERROR("Offset is out of range, index = %{public}d, signTable size = %{public}zu",
150 curIndex, signTable_.size());
151 return false;
152 }
153 return true;
154 }
155
CheckDataCopy(Instr * jitMemory,Byte * tmpBuffer,int size)156 int32_t JitCodeSigner::CheckDataCopy(Instr *jitMemory, Byte *tmpBuffer, int size)
157 {
158 if (jitMemory == nullptr) {
159 return CS_ERR_JIT_MEMORY;
160 }
161 if (tmpBuffer == nullptr) {
162 return CS_ERR_TMP_BUFFER;
163 }
164
165 // update tmp buffer
166 tmpBuffer_ = tmpBuffer;
167
168 if (((static_cast<uint32_t>(size) & UNALIGNMENT_MASK) != 0) ||
169 (static_cast<uint32_t>(size) > signTable_.size() * INSTRUCTION_SIZE)) {
170 #ifdef JIT_FORT_DISABLE
171 LOG_ERROR("Range invalid, size = %{public}d, table size = %{public}zu",
172 size, signTable_.size());
173 #else
174 char *buffer = reinterpret_cast<char *>(malloc(MAX_DEFERRED_LOG_LENGTH));
175 if (buffer == nullptr) {
176 return CS_ERR_OOM;
177 }
178
179 int ret = sprintf_s(buffer, MAX_DEFERRED_LOG_LENGTH,
180 "[%s]: Range invalid, size = %d, table size = %zu",
181 __func__, size, signTable_.size());
182 if (ret == -1) {
183 free(buffer);
184 buffer = nullptr;
185 return CS_ERR_LOG_TOO_LONG;
186 }
187
188 deferredLogs.emplace_back(DeferredLog{buffer});
189 #endif
190 return CS_ERR_JIT_SIGN_SIZE;
191 }
192 return CS_SUCCESS;
193 }
194
SignInstruction(Instr insn)195 void JitCodeSigner::SignInstruction(Instr insn)
196 {
197 int index = GetIndexFromOffset(offset_);
198 #ifdef JIT_CODE_SIGN_DEBUGGABLE
199 LOG_INFO("Offset = %{public}x, insn = %{public}x", offset_, insn);
200 if (static_cast<size_t>(index) != signTable_.size()) {
201 LOG_ERROR("Index = %{public}d not equal signtable size = %{public}zu.",
202 GetIndexFromOffset(offset_), signTable_.size());
203 }
204 #endif
205 signTable_.push_back(ctx_.SignSingle(insn, index));
206 offset_ += INSTRUCTION_SIZE;
207 }
208
SkipNext(uint32_t n)209 void JitCodeSigner::SkipNext(uint32_t n) {}
210
PatchInstruction(int offset,Instr insn)211 int32_t JitCodeSigner::PatchInstruction(int offset, Instr insn)
212 {
213 #ifdef JIT_CODE_SIGN_DEBUGGABLE
214 LOG_INFO("offset = %{public}x, insn = %{public}x", offset, insn);
215 #endif
216 int curIndex = 0;
217 if (!ConvertPatchOffsetToIndex(offset, curIndex)) {
218 LOG_ERROR("Offset invalid");
219 return CS_ERR_PATCH_INVALID;
220 }
221 uint32_t signature = ctx_.SignSingle(insn, curIndex);
222 signTable_[curIndex] = signature;
223 return CS_SUCCESS;
224 }
225
ValidateCodeCopy(Instr * jitMemory,Byte * tmpBuffer,int size)226 int32_t JitCodeSigner::ValidateCodeCopy(Instr *jitMemory,
227 Byte *tmpBuffer, int size)
228 {
229 int32_t ret = CheckDataCopy(jitMemory, tmpBuffer, size);
230 if (ret != CS_SUCCESS) {
231 return ret;
232 }
233
234 PACSignCtx verifyCtx(CTXPurpose::VERIFY, ctx_.GetSalt());
235 int offset = 0;
236 while (offset < size) {
237 int index = GetIndexFromOffset(offset);
238 Instr insn = *reinterpret_cast<const Instr *>(tmpBuffer_ + offset);
239 uint32_t signature = verifyCtx.SignSingle(insn, index);
240 if (signature != signTable_[index]) {
241 #ifdef JIT_FORT_DISABLE
242 LOG_ERROR("validate insn(%{public}x) without context failed at index = " \
243 "%{public}x, signature(%{public}x) != wanted(%{public}x)",
244 insn, index * INSTRUCTION_SIZE, signature, signTable_[index]);
245 #else
246 char *buffer = reinterpret_cast<char *>(malloc(MAX_DEFERRED_LOG_LENGTH));
247 if (buffer == nullptr) {
248 return CS_ERR_OOM;
249 }
250
251 int ret = sprintf_s(buffer, MAX_DEFERRED_LOG_LENGTH,
252 "[%s]: validate insn(%x) without context failed at index = " \
253 "%x, signature(%x) != wanted(%x)",
254 __func__, insn, index * INSTRUCTION_SIZE, signature, signTable_[index]);
255 if (ret == -1) {
256 free(buffer);
257 buffer = nullptr;
258 return CS_ERR_LOG_TOO_LONG;
259 }
260 deferredLogs.emplace_back(DeferredLog{buffer});
261 #endif
262 #ifndef JIT_CODE_SIGN_PERMISSIVE
263 return CS_ERR_VALIDATE_CODE;
264 #endif
265 }
266 *(jitMemory + index) = insn;
267 offset += INSTRUCTION_SIZE;
268 }
269 return CS_SUCCESS;
270 }
271 }
272 }
273 }