File Coverage

c-lib/s-bspatch.c
Criterion Covered Total %
statement 120 181 66.3
branch 68 148 45.9
condition n/a
subroutine n/a
pod n/a
total 188 329 57.1


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 316           a_bspatch_alloc(void *vp, size_t size){
35             struct s_bsdipa_patch_ctx *pcp;
36              
37 316           pcp = (struct s_bsdipa_patch_ctx*)vp;
38 316           vp = (*pcp->pc_mem.mc_alloc)(size);
39              
40 316           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 2868           a_bspatch_xin(uint8_t const *buf){
53             s_bsdipa_off_t y;
54              
55 2868           y = buf[0] & 0x7F;
56 2868           y <<= 8; y += buf[1];
57 2868           y <<= 8; y += buf[2];
58 2868           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 2868 100         if(buf[0] & 0x80)
66 8           y = -y;
67              
68 2868           return y;
69             }
70              
71             static inline int
72 480           a_bspatch_check_add_positive(s_bsdipa_off_t a, s_bsdipa_off_t b){
73             int rv;
74              
75 480           rv = (a < s_BSDIPA_OFF_MAX - b);
76              
77 480           return rv;
78             }
79              
80             static inline int
81 8           a_bspatch_check_add(s_bsdipa_off_t a, s_bsdipa_off_t b){
82             int rv;
83              
84 8           rv = 1;
85 8 50         if(b >= 0){
86 0 0         if(a >= s_BSDIPA_OFF_MAX - b)
87 0           rv = 0;
88 8 50         }else if(a < s_BSDIPA_OFF_MIN - b)
89 0           rv = 0;
90              
91 8           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 474           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 474           rv = s_BSDIPA_INVAL;
105              
106 474           x = a_bspatch_xin(dat);
107 474 50         if(x < 0)
108 0           goto jleave;
109 474 50         if(x & (sizeof(s_bsdipa_off_t) - 1))
110 0           goto jleave;
111 474 50         if(x % (sizeof(s_bsdipa_off_t) * 3))
112 0           goto jleave;
113 474           y = x; /* If we generated the header, data *plus* control block fits in _OFF_MAX! */
114 474           hp->h_ctrl_len = x;
115 474           dat += sizeof(x);
116              
117 474           x = a_bspatch_xin(dat);
118 474 50         if(x < 0)
119 0           goto jleave;
120 474 50         if(s_BSDIPA_OFF_MAX - y <= x)
121 0           goto jleave;
122 474           y += x;
123 474           hp->h_diff_len = x;
124 474           dat += sizeof(x);
125              
126 474           x = a_bspatch_xin(dat);
127 474 50         if(x < 0)
128 0           goto jleave;
129 474 50         if(s_BSDIPA_OFF_MAX - y <= x)
130 0           goto jleave;
131 474           hp->h_extra_len = x;
132 474           dat += sizeof(x);
133              
134 474           x = a_bspatch_xin(dat);
135 474 50         if(x < 0)
136 0           goto jleave;
137 474 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 474 50         if(x >= s_BSDIPA_OFF_MAX || (uint64_t)x >= SIZE_MAX / sizeof(s_bsdipa_off_t))
    50          
142 0           goto jleave;
143 474 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 474 50         if(x + 1 < hp->h_ctrl_len / ((s_bsdipa_off_t)sizeof(s_bsdipa_off_t) * 3))
148 0           goto jleave;
149             }
150 474           hp->h_before_len = x;
151              
152 474           rv = s_BSDIPA_OK;
153 474           jleave:
154 474           return rv;
155             }
156              
157             enum s_bsdipa_state
158 316           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 316 50         if(pcp->pc_mem.mc_alloc != NULL){
164 316           pcp->pc_mem.mc_custom_cookie = pcp;
165 316           pcp->pc_mem.mc_custom_alloc = &a_bspatch_alloc;
166 316           pcp->pc_mem.mc_custom_free = &a_bspatch_free;
167             }
168              
169             /* Enable s_bsdipa_patch_free() */
170 316           pcp->pc_restored_dat = NULL;
171 316           pcp->pc_restored_len = 0;
172              
173 316           rv = s_BSDIPA_INVAL;
174              
175 316 50         if(pcp->pc_patch_dat != NULL){
176             uint64_t x;
177              
178 316           pcp->pc_diff_dat = pcp->pc_ctrl_dat = pcp->pc_patch_dat;
179              
180 316           x = pcp->pc_patch_len;
181              
182 316           respos = pcp->pc_header.h_ctrl_len;
183 316 50         if(respos < 0 || x < (uint64_t)respos)
    50          
184 0           goto jleave;
185 316           x -= (uint64_t)respos;
186 316           pcp->pc_diff_dat += respos;
187              
188 316           respos = pcp->pc_header.h_diff_len;
189 316 50         if(respos < 0 || x < (uint64_t)respos)
    50          
190 0           goto jleave;
191 316           x -= (uint64_t)respos;
192 316           pcp->pc_diff_dat += respos;
193              
194 316           pcp->pc_extra_dat = pcp->pc_diff_dat;
195 316           respos = pcp->pc_header.h_extra_len;
196 316 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 316 50         if(pcp->pc_after_len >= s_BSDIPA_OFF_MAX)
218 0           goto jleave;
219              
220 316 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 316 50         }else if(respos < 0 || respos >= s_BSDIPA_OFF_MAX)
    50          
224 0           goto jleave;
225 316 50         else if(respos - pcp->pc_header.h_extra_len != pcp->pc_header.h_diff_len)
226 0           goto jleave;
227              
228 316           rv = s_BSDIPA_FBIG;
229              
230             /* 32-bit size_t excess? */
231 316 50         if((uint64_t)pcp->pc_header.h_before_len >= SIZE_MAX - 1)
232 0           goto jleave;
233              
234 316 100         if(pcp->pc_max_allowed_restored_len != 0 &&
235 158 50         pcp->pc_max_allowed_restored_len < (uint64_t)pcp->pc_header.h_before_len)
236 0           goto jleave;
237 316           pcp->pc_restored_len = pcp->pc_header.h_before_len;
238              
239             /* Ensure room for one additional byte, as documented. */
240 632           pcp->pc_restored_dat = (uint8_t*)(*pcp->pc_mem.mc_custom_alloc)(pcp->pc_mem.mc_custom_cookie,
241 316           (size_t)pcp->pc_restored_len +1);
242 316 50         if(pcp->pc_restored_dat == NULL){
243 0           rv = s_BSDIPA_NOMEM;
244 0           goto jleave;
245             }
246              
247 316           rv = s_BSDIPA_INVAL;
248              
249 640 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 324 50         if(pcp->pc_header.h_ctrl_len < (s_bsdipa_off_t)sizeof(s_bsdipa_off_t) * 3)
253 0           goto jleave;
254 324           pcp->pc_header.h_ctrl_len -= (s_bsdipa_off_t)sizeof(s_bsdipa_off_t) * 3;
255              
256 1296 100         for(i = 0; i < 3; ++i){
257 972           ctrl[i] = a_bspatch_xin(pcp->pc_ctrl_dat);
258 972           pcp->pc_ctrl_dat += sizeof(s_bsdipa_off_t);
259             }
260              
261 324 50         if((k = ctrl[1]) < 0 || k >= s_BSDIPA_OFF_MAX)
    50          
262 0           goto jleave;
263 324 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" (see s-bsdiff.c) */
267 324 100         if(any_tick && k == 0 && j == 0)
    50          
    50          
268 0           goto jleave;
269              
270             /* Add in diff */
271             /*j = ctrl[0];*/
272 324 100         if(j != 0){
273 156 50         if(pcp->pc_header.h_diff_len < j)
274 0           goto jleave;
275 156           pcp->pc_header.h_diff_len -= j;
276              
277 156 50         if(!a_bspatch_check_add_positive(respos, j) || respos + j > pcp->pc_header.h_before_len)
    50          
278 0           goto jleave;
279 156 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 1657456 100         while(j-- != 0)
283 1657300           pcp->pc_restored_dat[respos++] = *--pcp->pc_diff_dat + pcp->pc_after_dat[aftpos++];
284             }
285              
286             /* Extra data */
287 324           j = ctrl[1];
288 324 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 324           j = ctrl[2];
304 324 100         if(j != 0){
305 8 50         if(!a_bspatch_check_add(aftpos, j))
306 0           goto jleave;
307 8           aftpos += j;
308 8 50         if(aftpos < 0)
309 0           goto jleave;
310             }
311             }
312              
313 316 50         if(pcp->pc_header.h_ctrl_len == 0)
314 316           rv = s_BSDIPA_OK;
315              
316 0           jleave:
317 316           return rv;
318             }
319              
320             void
321 316           s_bsdipa_patch_free(struct s_bsdipa_patch_ctx *pcp){
322 316 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 316           }
325              
326             /* s-itt-mode */