]> Git Repo - u-boot.git/blame - cmd/setexpr.c
setexpr: Add some tests for buffer overflow and backref
[u-boot.git] / cmd / setexpr.c
CommitLineData
83d290c5 1// SPDX-License-Identifier: GPL-2.0+
d058698f
KG
2/*
3 * Copyright 2008 Freescale Semiconductor, Inc.
855f18ea 4 * Copyright 2013 Wolfgang Denk <[email protected]>
d058698f
KG
5 */
6
7/*
8 * This file provides a shell like 'expr' function to return.
9 */
10
11#include <common.h>
12#include <config.h>
13#include <command.h>
c7694dd4 14#include <env.h>
f7ae49fc 15#include <log.h>
2068cea1 16#include <mapmem.h>
d058698f 17
47ab5ad1
FM
18static ulong get_arg(char *s, int w)
19{
482126e2 20 /*
2068cea1
JH
21 * If the parameter starts with a '*' then assume it is a pointer to
22 * the value we want.
47ab5ad1 23 */
47ab5ad1 24 if (s[0] == '*') {
2068cea1
JH
25 ulong *p;
26 ulong addr;
27 ulong val;
28
29 addr = simple_strtoul(&s[1], NULL, 16);
47ab5ad1 30 switch (w) {
2068cea1
JH
31 case 1:
32 p = map_sysmem(addr, sizeof(uchar));
33 val = (ulong)*(uchar *)p;
34 unmap_sysmem(p);
35 return val;
36 case 2:
37 p = map_sysmem(addr, sizeof(ushort));
38 val = (ulong)*(ushort *)p;
39 unmap_sysmem(p);
40 return val;
47ab5ad1 41 case 4:
25a43ac8
SG
42 p = map_sysmem(addr, sizeof(u32));
43 val = *(u32 *)p;
44 unmap_sysmem(p);
45 return val;
2068cea1
JH
46 default:
47 p = map_sysmem(addr, sizeof(ulong));
48 val = *p;
49 unmap_sysmem(p);
50 return val;
47ab5ad1
FM
51 }
52 } else {
53 return simple_strtoul(s, NULL, 16);
54 }
55}
56
855f18ea
WD
57#ifdef CONFIG_REGEX
58
59#include <slre.h>
60
855f18ea
WD
61/*
62 * memstr - Find the first substring in memory
63 * @s1: The string to be searched
64 * @s2: The string to search for
65 *
66 * Similar to and based on strstr(),
67 * but strings do not need to be NUL terminated.
68 */
69static char *memstr(const char *s1, int l1, const char *s2, int l2)
70{
71 if (!l2)
72 return (char *)s1;
73
74 while (l1 >= l2) {
75 l1--;
76 if (!memcmp(s1, s2, l2))
77 return (char *)s1;
78 s1++;
79 }
80 return NULL;
81}
82
56331b26
SG
83/**
84 * substitute() - Substitute part of one string with another
85 *
86 * This updates @string so that the first occurrence of @old is replaced with
87 * @new
88 *
89 * @string: String buffer containing string to update at the start
90 * @slen: Pointer to current string length, updated on success
91 * @ssize: Size of string buffer
92 * @old: Old string to find in the buffer (no terminator needed)
93 * @olen: Length of @old excluding terminator
94 * @new: New string to replace @old with
95 * @nlen: Length of @new excluding terminator
96 * @return pointer to immediately after the copied @new in @string, or NULL if
97 * no replacement took place
98 */
99static char *substitute(char *string, int *slen, int ssize,
100 const char *old, int olen, const char *new, int nlen)
855f18ea
WD
101{
102 char *p = memstr(string, *slen, old, olen);
103
104 if (p == NULL)
105 return NULL;
106
107 debug("## Match at pos %ld: match len %d, subst len %d\n",
108 (long)(p - string), olen, nlen);
109
110 /* make sure replacement matches */
111 if (*slen + nlen - olen > ssize) {
112 printf("## error: substitution buffer overflow\n");
113 return NULL;
114 }
115
116 /* move tail if needed */
117 if (olen != nlen) {
118 int tail, len;
119
120 len = (olen > nlen) ? olen : nlen;
121
122 tail = ssize - (p + len - string);
123
124 debug("## tail len %d\n", tail);
125
126 memmove(p + nlen, p + olen, tail);
127 }
128
56331b26 129 /* insert substitute */
855f18ea
WD
130 memcpy(p, new, nlen);
131
132 *slen += nlen - olen;
133
134 return p + nlen;
135}
136
d422c77a
SG
137int setexpr_regex_sub(char *data, uint data_size, char *nbuf, uint nbuf_size,
138 const char *r, const char *s, bool global)
855f18ea
WD
139{
140 struct slre slre;
855f18ea 141 char *datap = data;
855f18ea
WD
142 int res, len, nlen, loop;
143
855f18ea
WD
144 if (slre_compile(&slre, r) == 0) {
145 printf("Error compiling regex: %s\n", slre.err_str);
146 return 1;
147 }
148
56331b26 149 len = strlen(data);
855f18ea
WD
150 if (s == NULL)
151 nlen = 0;
152 else
153 nlen = strlen(s);
154
155 for (loop = 0;; loop++) {
156 struct cap caps[slre.num_caps + 2];
855f18ea
WD
157 const char *old;
158 char *np;
159 int i, olen;
160
161 (void) memset(caps, 0, sizeof(caps));
162
163 res = slre_match(&slre, datap, len, caps);
164
165 debug("Result: %d\n", res);
166
167 for (i = 0; i < slre.num_caps; i++) {
168 if (caps[i].len > 0) {
169 debug("Substring %d: [%.*s]\n", i,
170 caps[i].len, caps[i].ptr);
171 }
172 }
173
174 if (res == 0) {
175 if (loop == 0) {
56331b26 176 printf("%s: No match\n", data);
855f18ea
WD
177 return 1;
178 } else {
179 break;
180 }
181 }
182
183 debug("## MATCH ## %s\n", data);
184
56331b26 185 if (!s)
855f18ea 186 return 1;
855f18ea
WD
187
188 old = caps[0].ptr;
189 olen = caps[0].len;
190
56331b26 191 if (nlen + 1 >= nbuf_size) {
855f18ea 192 printf("## error: pattern buffer overflow: have %d, need %d\n",
56331b26 193 nbuf_size, nlen + 1);
855f18ea
WD
194 return 1;
195 }
196 strcpy(nbuf, s);
197
198 debug("## SUBST(1) ## %s\n", nbuf);
199
200 /*
201 * Handle back references
202 *
203 * Support for \0 ... \9, where \0 is the
204 * whole matched pattern (similar to &).
205 *
206 * Implementation is a bit simpleminded as
207 * backrefs are substituted sequentially, one
208 * by one. This will lead to somewhat
209 * unexpected results if the replacement
210 * strings contain any \N strings then then
211 * may get substitued, too. We accept this
212 * restriction for the sake of simplicity.
213 */
214 for (i = 0; i < 10; ++i) {
215 char backref[2] = {
216 '\\',
217 '0',
218 };
219
220 if (caps[i].len == 0)
221 break;
222
223 backref[1] += i;
224
225 debug("## BACKREF %d: replace \"%.*s\" by \"%.*s\" in \"%s\"\n",
226 i,
227 2, backref,
228 caps[i].len, caps[i].ptr,
229 nbuf);
230
231 for (np = nbuf;;) {
232 char *p = memstr(np, nlen, backref, 2);
233
234 if (p == NULL)
235 break;
236
237 np = substitute(np, &nlen,
56331b26 238 nbuf_size,
855f18ea
WD
239 backref, 2,
240 caps[i].ptr, caps[i].len);
241
242 if (np == NULL)
243 return 1;
244 }
245 }
246 debug("## SUBST(2) ## %s\n", nbuf);
247
56331b26
SG
248 datap = substitute(datap, &len, data_size, old, olen,
249 nbuf, nlen);
855f18ea
WD
250
251 if (datap == NULL)
252 return 1;
253
254 debug("## REMAINDER: %s\n", datap);
255
256 debug("## RESULT: %s\n", data);
257
258 if (!global)
259 break;
260 }
382bee57 261 debug("## FINAL (now env_set()) : %s\n", data);
855f18ea 262
56331b26
SG
263 return 0;
264}
265
266#define SLRE_BUFSZ 16384
267#define SLRE_PATSZ 4096
268
269/*
270 * Perform regex operations on a environment variable
271 *
272 * Returns 0 if OK, 1 in case of errors.
273 */
274static int regex_sub_var(const char *name, const char *r, const char *s,
275 const char *t, int global)
276{
277 struct slre slre;
278 char data[SLRE_BUFSZ];
279 char nbuf[SLRE_PATSZ];
280 const char *value;
281 int len;
282 int ret;
283
284 if (!name)
285 return 1;
286
287 if (slre_compile(&slre, r) == 0) {
288 printf("Error compiling regex: %s\n", slre.err_str);
289 return 1;
290 }
291
292 if (!t) {
293 value = env_get(name);
294 if (!value) {
295 printf("## Error: variable \"%s\" not defined\n", name);
296 return 1;
297 }
298 t = value;
299 }
300
301 debug("REGEX on %s=%s\n", name, t);
302 debug("REGEX=\"%s\", SUBST=\"%s\", GLOBAL=%d\n", r, s ? s : "<NULL>",
303 global);
304
305 len = strlen(t);
306 if (len + 1 > SLRE_BUFSZ) {
307 printf("## error: subst buffer overflow: have %d, need %d\n",
308 SLRE_BUFSZ, len + 1);
309 return 1;
310 }
311
312 strcpy(data, t);
313
d422c77a
SG
314 ret = setexpr_regex_sub(data, SLRE_BUFSZ, nbuf, SLRE_PATSZ, r, s,
315 global);
56331b26
SG
316 if (ret)
317 return 1;
318
855f18ea
WD
319 printf("%s=%s\n", name, data);
320
382bee57 321 return env_set(name, data);
855f18ea
WD
322}
323#endif
324
09140113
SG
325static int do_setexpr(struct cmd_tbl *cmdtp, int flag, int argc,
326 char *const argv[])
d058698f
KG
327{
328 ulong a, b;
41ef372c 329 ulong value;
47ab5ad1 330 int w;
d058698f 331
855f18ea
WD
332 /*
333 * We take 3, 5, or 6 arguments:
334 * 3 : setexpr name value
335 * 5 : setexpr name val1 op val2
336 * setexpr name [g]sub r s
337 * 6 : setexpr name [g]sub r s t
338 */
339
340 /* > 6 already tested by max command args */
341 if ((argc < 3) || (argc == 4))
4c12eeb8 342 return CMD_RET_USAGE;
d058698f 343
47ab5ad1
FM
344 w = cmd_get_data_size(argv[0], 4);
345
346 a = get_arg(argv[2], w);
4823b45d 347
103c94b1 348 /* plain assignment: "setexpr name value" */
4823b45d 349 if (argc == 3) {
018f5303 350 env_set_hex(argv[1], a);
4823b45d
JH
351 return 0;
352 }
353
855f18ea
WD
354 /* 5 or 6 args (6 args only with [g]sub) */
355#ifdef CONFIG_REGEX
356 /*
357 * rexep handling: "setexpr name [g]sub r s [t]"
358 * with 5 args, "t" will be NULL
359 */
360 if (strcmp(argv[2], "gsub") == 0)
56331b26 361 return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 1);
855f18ea
WD
362
363 if (strcmp(argv[2], "sub") == 0)
56331b26 364 return regex_sub_var(argv[1], argv[3], argv[4], argv[5], 0);
855f18ea
WD
365#endif
366
103c94b1
WD
367 /* standard operators: "setexpr name val1 op val2" */
368 if (argc != 5)
369 return CMD_RET_USAGE;
370
371 if (strlen(argv[3]) != 1)
372 return CMD_RET_USAGE;
373
47ab5ad1 374 b = get_arg(argv[4], w);
d058698f
KG
375
376 switch (argv[3][0]) {
41ef372c
SG
377 case '|':
378 value = a | b;
379 break;
380 case '&':
381 value = a & b;
382 break;
383 case '+':
384 value = a + b;
385 break;
386 case '^':
387 value = a ^ b;
388 break;
389 case '-':
390 value = a - b;
391 break;
392 case '*':
393 value = a * b;
394 break;
395 case '/':
396 value = a / b;
397 break;
398 case '%':
399 value = a % b;
400 break;
d058698f
KG
401 default:
402 printf("invalid op\n");
403 return 1;
404 }
405
018f5303 406 env_set_hex(argv[1], value);
d058698f
KG
407
408 return 0;
409}
410
411U_BOOT_CMD(
855f18ea 412 setexpr, 6, 0, do_setexpr,
2fb2604d 413 "set environment variable as the result of eval expression",
4823b45d 414 "[.b, .w, .l] name [*]value1 <op> [*]value2\n"
d058698f 415 " - set environment variable 'name' to the result of the evaluated\n"
855f18ea 416 " expression specified by <op>. <op> can be &, |, ^, +, -, *, /, %\n"
4823b45d
JH
417 " size argument is only meaningful if value1 and/or value2 are\n"
418 " memory addresses (*)\n"
103c94b1
WD
419 "setexpr[.b, .w, .l] name [*]value\n"
420 " - load a value into a variable"
855f18ea
WD
421#ifdef CONFIG_REGEX
422 "\n"
423 "setexpr name gsub r s [t]\n"
424 " - For each substring matching the regular expression <r> in the\n"
425 " string <t>, substitute the string <s>. The result is\n"
426 " assigned to <name>. If <t> is not supplied, use the old\n"
427 " value of <name>\n"
428 "setexpr name sub r s [t]\n"
429 " - Just like gsub(), but replace only the first matching substring"
430#endif
d058698f 431);
This page took 0.374005 seconds and 4 git commands to generate.