File Coverage

c-lib/s-bspatch.c
Criterion Covered Total %
statement 111 181 61.3
branch 59 148 39.8
condition n/a
subroutine n/a
pod n/a
total 170 329 51.6


line stmt bran cond sub pod time code
1             /*@ Implementation of s-bsdipa-lib.h: s_bsdipa_patch() and support.
2             *
3             * Copyright (c) 2024 - 2026 Steffen Nurpmeso .
4             * SPDX-License-Identifier: ISC
5             *
6             * Permission to use, copy, modify, and/or distribute this software for any
7             * purpose with or without fee is hereby granted, provided that the above
8             * copyright notice and this permission notice appear in all copies.
9             *
10             * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11             * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12             * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13             * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14             * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15             * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16             * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17             */
18              
19             #include "s-bsdipa-lib.h"
20              
21             #include
22              
23             /* Compile-time state assertions to also ensure the below is correct are in s-bsdiff.c! */
24              
25             /* (Could be shared with s-bsdiff.c) */
26             static void *a_bspatch_alloc(void *vp, size_t size);
27             static void a_bspatch_free(void *vp, void *dat);
28              
29             static inline s_bsdipa_off_t a_bspatch_xin(uint8_t const *buf);
30             static inline int a_bspatch_check_add_positive(s_bsdipa_off_t a, s_bsdipa_off_t b);
31             static inline int a_bspatch_check_add(s_bsdipa_off_t a, s_bsdipa_off_t b);
32              
33             static void *
34 288           a_bspatch_alloc(void *vp, size_t size){
35             struct s_bsdipa_patch_ctx *pcp;
36              
37 288           pcp = (struct s_bsdipa_patch_ctx*)vp;
38 288           vp = (*pcp->pc_mem.mc_alloc)(size);
39              
40 288           return vp;
41             }
42              
43             static void
44 0           a_bspatch_free(void *vp, void *dat){
45             struct s_bsdipa_patch_ctx *pcp;
46              
47 0           pcp = (struct s_bsdipa_patch_ctx*)vp;
48 0           (*pcp->pc_mem.mc_free)(dat);
49 0           }
50              
51             static inline s_bsdipa_off_t
52 2592           a_bspatch_xin(uint8_t const *buf){
53             s_bsdipa_off_t y;
54              
55 2592           y = buf[0] & 0x7F;
56 2592           y <<= 8; y += buf[1];
57 2592           y <<= 8; y += buf[2];
58 2592           y <<= 8; y += buf[3];
59             #ifndef s_BSDIPA_32
60             y <<= 8; y += buf[4];
61             y <<= 8; y += buf[5];
62             y <<= 8; y += buf[6];
63             y <<= 8; y += buf[7];
64             #endif
65 2592 50         if(buf[0] & 0x80)
66 0           y = -y;
67              
68 2592           return y;
69             }
70              
71             static inline int
72 408           a_bspatch_check_add_positive(s_bsdipa_off_t a, s_bsdipa_off_t b){
73             int rv;
74              
75 408           rv = (a < s_BSDIPA_OFF_MAX - b);
76              
77 408           return rv;
78             }
79              
80             static inline int
81 0           a_bspatch_check_add(s_bsdipa_off_t a, s_bsdipa_off_t b){
82             int rv;
83              
84 0           rv = 1;
85 0 0         if(b >= 0){
86 0 0         if(a >= s_BSDIPA_OFF_MAX - b)
87 0           rv = 0;
88 0 0         }else if(a < s_BSDIPA_OFF_MIN - b)
89 0           rv = 0;
90              
91 0           return rv;
92             }
93              
94             s_bsdipa_off_t
95 0           s_bsdipa_buf_to_i(uint8_t const *in){
96 0           return a_bspatch_xin(in);
97             }
98              
99             enum s_bsdipa_state
100 432           s_bsdipa_patch_parse_header(struct s_bsdipa_header *hp, uint8_t const *dat){
101             s_bsdipa_off_t x, y;
102             enum s_bsdipa_state rv;
103              
104 432           rv = s_BSDIPA_INVAL;
105              
106 432           x = a_bspatch_xin(dat);
107 432 50         if(x < 0)
108 0           goto jleave;
109 432 50         if(x & (sizeof(s_bsdipa_off_t) - 1))
110 0           goto jleave;
111 432 50         if(x % (sizeof(s_bsdipa_off_t) * 3))
112 0           goto jleave;
113 432           y = x; /* If we generated the header, data *plus* control block fits in _OFF_MAX! */
114 432           hp->h_ctrl_len = x;
115 432           dat += sizeof(x);
116              
117 432           x = a_bspatch_xin(dat);
118 432 50         if(x < 0)
119 0           goto jleave;
120 432 50         if(s_BSDIPA_OFF_MAX - y <= x)
121 0           goto jleave;
122 432           y += x;
123 432           hp->h_diff_len = x;
124 432           dat += sizeof(x);
125              
126 432           x = a_bspatch_xin(dat);
127 432 50         if(x < 0)
128 0           goto jleave;
129 432 50         if(s_BSDIPA_OFF_MAX - y <= x)
130 0           goto jleave;
131 432           hp->h_extra_len = x;
132 432           dat += sizeof(x);
133              
134 432           x = a_bspatch_xin(dat);
135 432 50         if(x < 0)
136 0           goto jleave;
137 432 50         if(x == 0){
138 0 0         if(hp->h_ctrl_len != 0 || hp->h_diff_len != 0 || hp->h_extra_len != 0)
    0          
    0          
139 0           goto jleave;
140             }else{
141 432 50         if(x >= s_BSDIPA_OFF_MAX || (uint64_t)x >= SIZE_MAX / sizeof(s_bsdipa_off_t))
    50          
142 0           goto jleave;
143 432 50         if(x - hp->h_extra_len != hp->h_diff_len)
144 0           goto jleave;
145              
146             /* Since v0.9.0 bsdipa generates patches testable like so */
147 432 50         if(x + 1 < hp->h_ctrl_len / ((s_bsdipa_off_t)sizeof(s_bsdipa_off_t) * 3))
148 0           goto jleave;
149             }
150 432           hp->h_before_len = x;
151              
152 432           rv = s_BSDIPA_OK;
153 432           jleave:
154 432           return rv;
155             }
156              
157             enum s_bsdipa_state
158 288           s_bsdipa_patch(struct s_bsdipa_patch_ctx *pcp){
159             uint8_t any_tick;
160             s_bsdipa_off_t aftpos, respos, ctrl[3];
161             enum s_bsdipa_state rv;
162              
163 288 50         if(pcp->pc_mem.mc_alloc != NULL){
164 288           pcp->pc_mem.mc_custom_cookie = pcp;
165 288           pcp->pc_mem.mc_custom_alloc = &a_bspatch_alloc;
166 288           pcp->pc_mem.mc_custom_free = &a_bspatch_free;
167             }
168              
169             /* Enable s_bsdipa_patch_free() */
170 288           pcp->pc_restored_dat = NULL;
171 288           pcp->pc_restored_len = 0;
172              
173 288           rv = s_BSDIPA_INVAL;
174              
175 288 50         if(pcp->pc_patch_dat != NULL){
176             uint64_t x;
177              
178 288           pcp->pc_diff_dat = pcp->pc_ctrl_dat = pcp->pc_patch_dat;
179              
180 288           x = pcp->pc_patch_len;
181              
182 288           respos = pcp->pc_header.h_ctrl_len;
183 288 50         if(respos < 0 || x < (uint64_t)respos)
    50          
184 0           goto jleave;
185 288           x -= (uint64_t)respos;
186 288           pcp->pc_diff_dat += respos;
187              
188 288           respos = pcp->pc_header.h_diff_len;
189 288 50         if(respos < 0 || x < (uint64_t)respos)
    50          
190 0           goto jleave;
191 288           x -= (uint64_t)respos;
192 288           pcp->pc_diff_dat += respos;
193              
194 288           pcp->pc_extra_dat = pcp->pc_diff_dat;
195 288           respos = pcp->pc_header.h_extra_len;
196 288 50         if(respos < 0 || x < (uint64_t)respos)
    50          
197 0           goto jleave;
198             /* xxx Do not care about excess data in patch? */
199             }else{
200 0           respos = pcp->pc_header.h_ctrl_len;
201 0 0         if(respos < 0 || respos >= s_BSDIPA_OFF_MAX)
    0          
202 0           goto jleave;
203 0           respos = pcp->pc_header.h_diff_len;
204 0 0         if(respos < 0 || respos >= s_BSDIPA_OFF_MAX)
    0          
205 0           goto jleave;
206 0           aftpos = pcp->pc_header.h_extra_len;
207 0 0         if(aftpos < 0 || aftpos >= s_BSDIPA_OFF_MAX)
    0          
208 0           goto jleave;
209 0 0         if(s_BSDIPA_OFF_MAX - aftpos <= respos)
210 0           goto jleave;
211 0           respos += aftpos;
212 0 0         if(s_BSDIPA_OFF_MAX - respos <= pcp->pc_header.h_ctrl_len)
213 0           goto jleave;
214             }
215              
216             /* The effective limit is smaller, but that is up to diff generation: "just do it" */
217 288 50         if(pcp->pc_after_len >= s_BSDIPA_OFF_MAX)
218 0           goto jleave;
219              
220 288 50         if((respos = pcp->pc_header.h_before_len) == 0){
221 0 0         if(pcp->pc_header.h_ctrl_len != 0 || pcp->pc_header.h_diff_len != 0 || pcp->pc_header.h_extra_len != 0)
    0          
    0          
222 0           goto jleave;
223 288 50         }else if(respos < 0 || respos >= s_BSDIPA_OFF_MAX)
    50          
224 0           goto jleave;
225 288 50         else if(respos - pcp->pc_header.h_extra_len != pcp->pc_header.h_diff_len)
226 0           goto jleave;
227              
228 288           rv = s_BSDIPA_FBIG;
229              
230             /* 32-bit size_t excess? */
231 288 50         if((uint64_t)pcp->pc_header.h_before_len >= SIZE_MAX - 1)
232 0           goto jleave;
233              
234 288 100         if(pcp->pc_max_allowed_restored_len != 0 &&
235 144 50         pcp->pc_max_allowed_restored_len < (uint64_t)pcp->pc_header.h_before_len)
236 0           goto jleave;
237 288           pcp->pc_restored_len = pcp->pc_header.h_before_len;
238              
239             /* Ensure room for one additional byte, as documented. */
240 576           pcp->pc_restored_dat = (uint8_t*)(*pcp->pc_mem.mc_custom_alloc)(pcp->pc_mem.mc_custom_cookie,
241 288           (size_t)pcp->pc_restored_len +1);
242 288 50         if(pcp->pc_restored_dat == NULL){
243 0           rv = s_BSDIPA_NOMEM;
244 0           goto jleave;
245             }
246              
247 288           rv = s_BSDIPA_INVAL;
248              
249 576 100         for(any_tick = 0, aftpos = respos = 0; respos < pcp->pc_header.h_before_len; any_tick = 1){
250             s_bsdipa_off_t i, j, k;
251              
252 288 50         if(pcp->pc_header.h_ctrl_len < (s_bsdipa_off_t)sizeof(s_bsdipa_off_t) * 3)
253 0           goto jleave;
254 288           pcp->pc_header.h_ctrl_len -= (s_bsdipa_off_t)sizeof(s_bsdipa_off_t) * 3;
255              
256 1152 100         for(i = 0; i < 3; ++i){
257 864           ctrl[i] = a_bspatch_xin(pcp->pc_ctrl_dat);
258 864           pcp->pc_ctrl_dat += sizeof(s_bsdipa_off_t);
259             }
260              
261 288 50         if((k = ctrl[1]) < 0 || k >= s_BSDIPA_OFF_MAX)
    50          
262 0           goto jleave;
263 288 50         if((j = ctrl[0]) < 0 || j >= s_BSDIPA_OFF_MAX)
    50          
264 0           goto jleave;
265              
266             /* A data-less control (but the first) is "malicious" */
267 288 50         if(any_tick && k == 0 && j == 0)
    0          
    0          
268 0           goto jleave;
269              
270             /* Add in diff */
271             /*j = ctrl[0];*/
272 288 100         if(j != 0){
273 120 50         if(pcp->pc_header.h_diff_len < j)
274 0           goto jleave;
275 120           pcp->pc_header.h_diff_len -= j;
276              
277 120 50         if(!a_bspatch_check_add_positive(respos, j) || respos + j > pcp->pc_header.h_before_len)
    50          
278 0           goto jleave;
279 120 50         if(!a_bspatch_check_add_positive(aftpos, j) || aftpos + j > (s_bsdipa_off_t)pcp->pc_after_len)
    50          
280 0           goto jleave;
281              
282 1225128 100         while(j-- != 0)
283 1225008           pcp->pc_restored_dat[respos++] = *--pcp->pc_diff_dat + pcp->pc_after_dat[aftpos++];
284             }
285              
286             /* Extra dat */
287 288           j = ctrl[1];
288 288 100         if(j != 0){
289 168 50         if(pcp->pc_header.h_extra_len < j)
290 0           goto jleave;
291 168           pcp->pc_header.h_extra_len -= j;
292              
293 168 50         if(!a_bspatch_check_add_positive(respos, j) || respos + j > pcp->pc_header.h_before_len)
    50          
294 0           goto jleave;
295              
296 168           memcpy(&pcp->pc_restored_dat[respos], pcp->pc_extra_dat, (size_t)j);
297 168           pcp->pc_extra_dat += j;
298              
299 168           respos += j;
300             }
301              
302             /**/
303 288           j = ctrl[2];
304 288 50         if(j != 0){
305 0 0         if(!a_bspatch_check_add(aftpos, j))
306 0           goto jleave;
307 0           aftpos += j;
308 0 0         if(aftpos < 0)
309 0           goto jleave;
310             }
311             }
312              
313 288 50         if(pcp->pc_header.h_ctrl_len == 0)
314 288           rv = s_BSDIPA_OK;
315              
316 0           jleave:
317 288           return rv;
318             }
319              
320             void
321 288           s_bsdipa_patch_free(struct s_bsdipa_patch_ctx *pcp){
322 288 50         if(pcp->pc_restored_dat != NULL)
323 0           (*pcp->pc_mem.mc_custom_free)(pcp->pc_mem.mc_custom_cookie, pcp->pc_restored_dat);
324 288           }
325              
326             /* s-itt-mode */