• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }