### Bug Summary

File: | builds/wireshark/wireshark/epan/wslua/wslua_struct.c
Warning: | line 544, column 19 Subtraction of a null pointer (from variable 'e') and a non-null pointer results in undefined behavior

### Annotated Source Code

Press '?' to see keyboard shortcuts

Show analyzer invocation
clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name wslua_struct.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-22/lib/clang/22 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /usr/include/lua5.5 -isystem /builds/wireshark/wireshark/build/epan/wslua -isystem /builds/wireshark/wireshark/epan/wslua -D CARES_NO_DEPRECATED -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D WS_BUILD_DLL -D WS_DEBUG -D WS_DEBUG_UTF_8 -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-22/lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-nonliteral -std=gnu17 -ferror-limit 19 -fvisibility=hidden -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /builds/wireshark/wireshark/sbout/2026-07-20-100411-3661-1 -x c /builds/wireshark/wireshark/epan/wslua/wslua_struct.c

Keyboard shortcuts:

- Use 'j/k' keys for keyboard navigation
- Use 'Shift+S' to show/hide relevant lines
- Use '?' to toggle this window

Close
Show only relevant lines Show control flow arrows

1 | /******************************************************************************
2 | * Copyright (C) 2010-2012 Lua.org, PUC-Rio. All rights reserved.
3 | *
4 | * SPDX-License-Identifier: MIT
5 | *
6 | ******************************************************************************/
7 | /*
8 | ** {======================================================
9 | ** Library for packing/unpacking structures.
10 | ** See Copyright Notice above.
11 | **
12 | ** Small changes were made by Hadriel Kaplan - those changes
13 | ** are in the Public Domain.
14 | **
15 | ** Some changes are based on a patch to struct.h from
16 | ** Flemming Madsen, from here:
17 | ** http://lua-users.org/lists/lua-l/2009-10/msg00572.html
18 | ** In particular, these changes from him:
19 | ** -Can handle 'long long' integers (i8 / I8); though they're converted to doubles
20 | ** -Can insert/specify padding anywhere in a struct. ('X' eg. when a string is following a union)
21 | ** -Can report current offset in both pack and unpack ('=')
22 | ** -Can mask out return values when you only want to calculate sizes or unmarshal pascal-style strings. '(' & ')'
23 | **
24 | ** Changes I made:
25 | ** -Added support for Int64/UInt64 being packed/unpacked, using 'e'/'E'
26 | ** -Made it follow Wireshark's conventions so we could get API docs
27 | ** =======================================================
28 | */
29 | /*
30 | ** Valid formats:
31 | ** > - big endian
32 | ** < - little endian
33 | ** ![num] - alignment
34 | ** x[num] - pad num bytes, default 1
35 | ** X[num] - pad to num align, default MAXALIGN
36 | **
37 | ** Following are system-dependent sizes:
38 | ** i/I - signed/unsigned int
39 | ** l/L - signed/unsigned long
40 | ** f - float
41 | ** T - size_t
42 | **
43 | ** Following are system-independent sizes:
44 | ** b/B - signed/unsigned byte
45 | ** h/H - signed/unsigned short
46 | ** in/In - signed/unsigned integer of size `n' bytes
47 | Note: Unpack of i/I is done to a Lua_number, typically a double,
48 | so unpacking a 64-bit field (i8/I8) will lose precision.
49 | Use e/E to unpack into a Wireshark Int64/UInt64 object/userdata instead.
50 | ** e/E - signed/unsigned eight-byte Integer (64bits, long long), to/from Int64/UInt64 object
51 | ** d - double
52 | ** cn - sequence of `n' chars (from/to a string); when packing, n==0 means
53 | the whole string; when unpacking, n==0 means use the previous
54 | read number as the string length
55 | ** s - zero-terminated string
56 | ** ' ' - ignored
57 | ** '(' ')' - stop assigning items. ')' start assigning (padding when packing)
58 | ** '=' - return current position / offset
59 | */
60 |
61 | #include "config.h"
62 |
63 | #include <limits.h>
64 | #include <wsutil/array.h>
65 | #include "wslua.h"
66 |
67 | /* WSLUA_MODULE Struct Binary encode/decode support
68 |
69 | The Struct class offers basic facilities to convert Lua values to and from C-style structs
70 | in binary Lua strings. This is based on Roberto Ierusalimschy's Lua struct library found
71 | in http://www.inf.puc-rio.br/~roberto/struct/, with some minor modifications as follows:
72 | * Added support for `Int64`/`UInt64` being packed/unpacked, using 'e'/'E'.
73 | * Can handle 'long long' integers (i8 / I8); though they're converted to doubles.
74 | * Can insert/specify padding anywhere in a struct. ('X' eg. when a string is following a union).
75 | * Can report current offset in both `pack` and `unpack` ('`=`').
76 | * Can mask out return values when you only want to calculate sizes or unmarshal
77 | pascal-style strings using '`(`' & '`)`'.
78 |
79 | All but the first of those changes are based on an email from Flemming Madsen, on the lua-users
80 | mailing list, which can be found http://lua-users.org/lists/lua-l/2009-10/msg00572.html[here].
81 |
82 | The main functions are `Struct.pack`, which packs multiple Lua values into a struct-like
83 | Lua binary string; and `Struct.unpack`, which unpacks multiple Lua values from a given
84 | struct-like Lua binary string. There are some additional helper functions available as well.
85 |
86 | All functions in the Struct library are called as static member functions, not object methods,
87 | so they are invoked as "Struct.pack(...)" instead of "object:pack(...)".
88 |
89 | The first argument to several of the `Struct` functions is a format string, which describes
90 | the layout of the structure. The format string is a sequence of conversion elements, which
91 | respect the current endianness and the current alignment requirements. Initially, the
92 | current endianness is the machine's native endianness and the current alignment requirement
93 | is 1 (meaning no alignment at all). You can change these settings with appropriate directives
94 | in the format string.
95 |
96 | The supported elements in the format string are as follows:
97 |
98 | * `$$ $$' (empty space) ignored.
99 | * `++!++__n__' flag to set the current alignment requirement to 'n' (necessarily a power of 2);
100 | an absent 'n' means the machine's native alignment.
101 | * `++>++' flag to set mode to big endian (i.e., network-order).
102 | * `++<++' flag to set mode to little endian.
103 | * `++x++' a padding zero byte with no corresponding Lua value.
104 | * `++b++' a signed char.
105 | * `++B++' an unsigned char.
106 | * `++h++' a signed short (native size).
107 | * `++H++' an unsigned short (native size).
108 | * `++l++' a signed long (native size).
109 | * `++L++' an unsigned long (native size).
110 | * `++T++' a size_t (native size).
111 | * `++i++__n__' a signed integer with 'n' bytes. An absent 'n' means the native size of an int.
112 | * `++I++__n__' like `++i++__n__' but unsigned.
113 | * `++e++' signed 8-byte Integer (64-bits, long long), to/from a +Int64+ object.
114 | * `++E++' unsigned 8-byte Integer (64-bits, long long), to/from a +UInt64+ object.
115 | * `++f++' a float (native size).
116 | * `++d++' a double (native size).
117 | * `++s++' a zero-terminated string.
118 | * `++c++__n__' a sequence of exactly 'n' chars corresponding to a single Lua string. An absent 'n'
119 | means 1. When packing, the given string must have at least 'n' characters (extra
120 | characters are discarded).
121 | * `++c0++' this is like `++c++__n__', except that the 'n' is given by other means: When packing, 'n' is
122 | the length of the given string; when unpacking, 'n' is the value of the previous unpacked
123 | value (which must be a number). In that case, this previous value is not returned.
124 | * `++x++__n__' pad to 'n' number of bytes, default 1.
125 | * `++X++__n__' pad to 'n' alignment, default MAXALIGN.
126 | * `++(++' to stop assigning items, and `++)++' start assigning (padding when packing).
127 | * `++=++' to return the current position / offset.
128 |
129 | [IMPORTANT]
130 | ====
131 | Using `i`, `I`, `h`, `H`, `l`, `L`, `f`, and `T` is strongly discouraged, as those sizes
132 | are system-dependent. Use the explicitly sized variants instead, such as `i4` or `E`.
133 |
134 | Unpacking of `i`/`I` is done to a Lua number, a double-precision floating point,
135 | so unpacking a 64-bit field (`i8`/`I8`) will lose precision.
136 | Use `e`/`E` to unpack into a Wireshark `Int64`/`UInt64` object instead.
137 | ====
138 |
139 | [NOTE]
140 | ====
141 | Lua 5.3 and later provides several built-in functions for struct unpacking and packing:
142 | https://www.lua.org/manual/5.4/manual.html#pdf-string.pack[string.pack],
143 | https://www.lua.org/manual/5.4/manual.html#pdf-string.packsize[string.packsize], and
144 | https://www.lua.org/manual/5.4/manual.html#pdf-string.unpack[string.unpack].
145 | You can use those as well, but note that the
146 | https://www.lua.org/manual/5.4/manual.html#6.4.2[format string] conversion elements
147 | are slightly different, and they do not support the Wireshark `Int64`/`UInt64` objects.
148 | ====
149 | */
150 |
151 |
152 | /* The following line is here so that make-reg.py does the right thing. This 'Struct' class
153 | isn't really a class, so it doesn't have the checkStruct/pushStruct/etc. functions
154 | the following macro would generate; but it does need to be registered and such, so...
155 | WSLUA_CLASS_DEFINE_BASE(Struct,NOP,0);
156 | */
157 |
158 | /* basic integer type - yes this is system-specific size - it's meant to be */
159 | #if !defined(STRUCT_INTlong)
160 | #define STRUCT_INTlong long
161 | #endif
162 |
163 | typedef STRUCT_INTlong Inttype;
164 |
165 | /* corresponding unsigned version */
166 | typedef unsigned STRUCT_INTlong Uinttype;
167 |
168 | /* maximum size (in bytes) for integral types */
169 | #define MAXINTSIZE32 32
170 |
171 | /* is 'x' a power of 2? */
172 | #define isp2(x)((x) > 0 && ((x) & ((x) - 1)) == 0) ((x) > 0 && ((x) & ((x) - 1)) == 0)
173 |
174 | /* dummy structure to get padding/alignment requirements */
175 | struct cD {
176 | char c;
177 | double d;
178 | };
179 |
180 |
181 | #define PADDING(sizeof(struct cD) - sizeof(double)) (sizeof(struct cD) - sizeof(double))
182 | #define MAXALIGN((sizeof(struct cD) - sizeof(double)) > sizeof(int) ? (sizeof (struct cD) - sizeof(double)) : sizeof(int)) (PADDING(sizeof(struct cD) - sizeof(double)) > sizeof(int) ? PADDING(sizeof(struct cD) - sizeof(double)) : sizeof(int))
183 |
184 |
185 | /* endian options */
186 | #define BIG0 0
187 | #define LITTLE1 1
188 |
189 | /* trick to determine native endianness of system */
190 | static union {
191 | int dummy;
192 | char endian;
193 | } const native = {1};
194 |
195 | /* settings info */
196 | typedef struct Header {
197 | int endian;
198 | int align;
199 | bool_Bool noassign;
200 | } Header;
201 |
202 | /* For options that take a number argument, gets the number */
203 | static int getnum (lua_State *L, const char **fmt, int df) {
204 | if (!g_ascii_isdigit(**fmt)((g_ascii_table[(guchar) (**fmt)] & G_ASCII_DIGIT) != 0)) /* no number? */
205 | return df; /* return default value */
206 | else {
207 | int a = 0;
208 | do {
209 | if (a > (INT_MAX2147483647 / 10) || a * 10 > (INT_MAX2147483647 - (**fmt - '0')))
210 | luaL_error(L, "integral size overflow");
211 | a = a*10 + *((*fmt)++) - '0';
212 | } while (g_ascii_isdigit(**fmt)((g_ascii_table[(guchar) (**fmt)] & G_ASCII_DIGIT) != 0));
213 | return a;
214 | }
215 | }
216 |
217 |
218 | #define defaultoptions(h)((h)->endian = native.endian, (h)->align = 1, (h)->noassign = 0) ((h)->endian = native.endian, (h)->align = 1, (h)->noassign = false0)
219 |
220 |
221 | /* gets size (number of bytes) for a given type */
222 | static size_t optsize (lua_State *L, char opt, const char **fmt) {
223 | switch (opt) {
224 | case 'B': case 'b': return sizeof(char);
225 | case 'H': case 'h': return sizeof(short);
226 | case 'L': case 'l': return sizeof(long);
227 | case 'E': case 'e': return sizeof(int64_t);
228 | case 'T': return sizeof(size_t);
229 | case 'f': return sizeof(float);
230 | case 'd': return sizeof(double);
231 | case 'x': return getnum(L, fmt, 1);
232 | case 'X': return getnum(L, fmt, MAXALIGN((sizeof(struct cD) - sizeof(double)) > sizeof(int) ? (sizeof (struct cD) - sizeof(double)) : sizeof(int)));
233 | case 'c': return getnum(L, fmt, 1);
234 | case 'i': case 'I': {
235 | int sz = getnum(L, fmt, sizeof(int));
236 | if (sz > MAXINTSIZE32)
237 | luaL_error(L, "integral size %d is larger than limit of %d",
238 | sz, MAXINTSIZE32);
239 | return sz;
240 | }
241 | case 's': case ' ':
242 | case '<': case '>':
243 | case '(': case ')':
244 | case '!': case '=':
245 | return 0; /* these cases do not have a size */
246 | default: {
247 | const char *msg = lua_pushfstring(L, "invalid format option [%c]", opt);
248 | return luaL_argerror(L, 1, msg);
249 | }
250 | }
251 | }
252 |
253 |
254 | /*
255 | ** return number of bytes needed to align an element of size 'size'
256 | ** at current position 'len'
257 | */
258 | static int gettoalign (size_t len, Header *h, int opt, size_t size) {
259 | if (size == 0 || opt == 'c' || opt == 's') return 0;
3 | ← | Assuming 'size' is equal to 0 | →
4 | ← | Returning without writing to 'h->noassign', which participates in a condition later | →
260 | if (size > (size_t)h->align)
261 | size = h->align; /* respect max. alignment */
262 | return (int)((size - (len & (size - 1))) & (size - 1));
263 | }
264 |
265 |
266 | /*
267 | ** options to control endianness and alignment settings
268 | */
269 | static void controloptions (lua_State *L, int opt, const char **fmt,
270 | Header *h) {
271 | switch (opt) {
272 | case ' ': return; /* ignore white spaces */
273 | case '>': h->endian = BIG0; return;
274 | case '<': h->endian = LITTLE1; return;
275 | case '(': h->noassign = true1; return;
276 | case ')': h->noassign = false0; return;
277 | case '!': {
278 | int a = getnum(L, fmt, MAXALIGN((sizeof(struct cD) - sizeof(double)) > sizeof(int) ? (sizeof (struct cD) - sizeof(double)) : sizeof(int)));
279 | if (!isp2(a)((a) > 0 && ((a) & ((a) - 1)) == 0))
280 | luaL_error(L, "alignment %d is not a power of 2", a);
281 | h->align = a;
282 | return;
283 | }
284 | default: {
285 | const char *msg = lua_pushfstring(L, "invalid format option '%c'", opt);
286 | luaL_argerror(L, 1, msg);
287 | }
288 | }
289 | }
290 |
291 | /* Encodes a Lua number as an integer of given size and endianness into a string struct */
292 | static void putinteger (lua_State *L, luaL_Buffer *b, int arg, int endian,
293 | int size) {
294 | lua_Number n = luaL_checknumber(L, arg);
295 | /* this one's not system dependent size - it's a long long */
296 | int64_t value;
297 | char buff[MAXINTSIZE32];
298 | if (n < 0)
299 | value = (uint64_t)(int64_t)n;
300 | else
301 | value = (uint64_t)n;
302 | if (endian == LITTLE1) {
303 | int i;
304 | for (i = 0; i < size; i++) {
305 | buff[i] = (value & 0xff);
306 | value >>= 8;
307 | }
308 | }
309 | else {
310 | int i;
311 | for (i = size - 1; i >= 0; i--) {
312 | buff[i] = (value & 0xff);
313 | value >>= 8;
314 | }
315 | }
316 | luaL_addlstring(b, buff, size);
317 | }
318 |
319 | /* corrects endianness - usually done by other functions themselves, but is
320 | * used for float/doubles, since on some platforms they're endian'ed as well
321 | */
322 | static void correctbytes (char *b, int size, int endian) {
323 | if (endian != native.endian) {
324 | int i = 0;
325 | while (i < --size) {
326 | char temp = b[i];
327 | b[i++] = b[size];
328 | b[size] = temp;
329 | }
330 | }
331 | }
332 |
333 |
334 | WSLUA_CONSTRUCTORstatic int Struct_pack (lua_State *L) {
335 | /* Returns a string containing the values arg1, arg2, etc. packed/encoded according to the format string. */
336 | #define WSLUA_ARG_Struct_pack_FORMAT1 1 /* The format string */
337 | #define WSLUA_ARG_Struct_pack_VALUE2 2 /* One or more Lua value(s) to encode, based on the given format. */
338 | luaL_Buffer b;
339 | const char *fmt = wslua_checkstring_only(L, WSLUA_ARG_Struct_pack_FORMAT1);
340 | Header h;
341 | int poscnt = 0;
342 | int posBuf[10];
343 | int arg = 2;
344 | size_t totalsize = 0;
345 | defaultoptions(&h)((&h)->endian = native.endian, (&h)->align = 1, (&h)->noassign = 0);
346 | lua_pushnil(L); /* mark to separate arguments from string buffer */
347 | luaL_buffinit(L, &b);
348 | while (*fmt != '\0') {
349 | int opt = *fmt++;
350 | size_t size = optsize(L, opt, &fmt);
351 | int toalign = gettoalign(totalsize, &h, opt, size);
352 | totalsize += toalign;
353 | while (toalign-- > 0) luaL_addchar(&b, '\0')((void)((&b)->n < (&b)->size || luaL_prepbuffsize ((&b), 1)), ((&b)->b[(&b)->n++] = ('\0')));
354 | if (opt == 'X') size = 0; /* 'X' is about alignment, not size */
355 | if (h.noassign && size) opt = 'x'; /* for pack, "(i4)" is the same as "x4" */
356 | switch (opt) {
357 | case 'b': case 'B': case 'h': case 'H':
358 | case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */
359 | putinteger(L, &b, arg++, h.endian, (int)size);
360 | break;
361 | }
362 | case 'e': {
363 | Int64_pack(L, &b, arg++, h.endian == LITTLE1);
364 | break;
365 | }
366 | case 'E': {
367 | UInt64_pack(L, &b, arg++, h.endian == LITTLE1);
368 | break;
369 | }
370 | case 'x': case 'X': {
371 | size_t len = size;
372 | while (len-- > 0)
373 | luaL_addchar(&b, '\0')((void)((&b)->n < (&b)->size || luaL_prepbuffsize ((&b), 1)), ((&b)->b[(&b)->n++] = ('\0')));
374 | break;
375 | }
376 | case 'f': {
377 | float f = (float)luaL_checknumber(L, arg++);
378 | correctbytes((char *)&f, (int)size, h.endian);
379 | luaL_addlstring(&b, (char *)&f, size);
380 | break;
381 | }
382 | case 'd': {
383 | double d = luaL_checknumber(L, arg++);
384 | correctbytes((char *)&d, (int)size, h.endian);
385 | luaL_addlstring(&b, (char *)&d, size);
386 | break;
387 | }
388 | case 'c': case 's': {
389 | size_t l;
390 | const char *s = luaL_checklstring(L, arg++, &l);
391 | if (size == 0) size = l;
392 | luaL_argcheck(L, l >= (size_t)size, arg, "string too short")((void)((__builtin_expect(((l >= (size_t)size) != 0), 1)) || luaL_argerror(L, (arg), ("string too short"))));
393 | luaL_addlstring(&b, s, size);
394 | if (opt == 's') {
395 | luaL_addchar(&b, '\0')((void)((&b)->n < (&b)->size || luaL_prepbuffsize ((&b), 1)), ((&b)->b[(&b)->n++] = ('\0'))); /* add zero at the end */
396 | size++;
397 | }
398 | break;
399 | }
400 | case '=': {
401 | if (poscnt < (int)array_length(posBuf)(sizeof (posBuf) / sizeof (posBuf)[0]))
402 | posBuf[poscnt++] = (int)totalsize + 1;
403 | break;
404 | }
405 | default: controloptions(L, opt, &fmt, &h);
406 | }
407 | totalsize += size;
408 | }
409 | luaL_pushresult(&b);
410 | for (arg = 0; arg < poscnt; arg++)
411 | lua_pushinteger(L, posBuf[arg]);
412 | WSLUA_RETURN(poscnt + 1)return (poscnt + 1); /* The packed binary Lua string, plus any positions due to '=' being used in format. */
413 | }
414 |
415 | static Uinttype decodeinteger (const char *buff, int endian, int size)
416 | {
417 | Uinttype l = 0;
418 | int i;
419 | if (endian == BIG0) {
420 | for (i = 0; i < size; i++) {
421 | l <<= 8;
422 | l |= (Uinttype)(unsigned char)buff[i];
423 | }
424 | }
425 | else {
426 | for (i = size - 1; i >= 0; i--) {
427 | l <<= 8;
428 | l |= (Uinttype)(unsigned char)buff[i];
429 | }
430 | }
431 | return l;
432 | }
433 |
434 | /* Decodes an integer from a string struct into a lua_Integer, if it fits
435 | * without truncation, or a lua_Number, based on given endianness and size.
436 | * If the integer type is signed, that is handled correctly as well.
437 | * Note for large values of size there can be a loss of precision.
438 | */
439 | static void getinteger (lua_State *L, const char *buff, int endian,
440 | int issigned, int size) {
441 | Uinttype l = decodeinteger(buff, endian, size);
442 | if (!issigned) {
443 | if (size < LUA_INTEGER_SIZE8) {
444 | /* Fits in a lua_Integer (we need a larger size as lua_Integer
445 | * is signed.) */
446 | lua_pushinteger(L, (lua_Integer)l);
447 | } else {
448 | /* Does not fit in a lua_Integer */
449 | lua_pushnumber(L, (lua_Number)l);
450 | }
451 | }
452 | else { /* signed format */
453 | Uinttype mask = (Uinttype)(~((Uinttype)0)) << (size*8 - 1);
454 | if (l & mask) /* negative value? */
455 | l |= mask; /* sign extension */
456 | if (size <= LUA_INTEGER_SIZE8) {
457 | /* Fits in a lua_Integer */
458 | lua_pushinteger(L, (lua_Integer)(Inttype)l);
459 | } else {
460 | /* Does not fit in a lua_Integer */
461 | lua_pushnumber(L, (lua_Number)(Inttype)l);
462 | }
463 | }
464 | }
465 |
466 | #define b_pushnumber(n){ if (!h.noassign) lua_pushnumber(L, (lua_Number)(n)); } { if (!h.noassign) lua_pushnumber(L, (lua_Number)(n)); }
467 |
468 | WSLUA_CONSTRUCTORstatic int Struct_unpack (lua_State *L) {
469 | /* Unpacks/decodes multiple Lua values from a given struct-like binary Lua string.
470 | The number of returned values depends on the format given, plus an additional value of the position where it stopped reading is returned. */
471 | #define WSLUA_ARG_Struct_unpack_FORMAT1 1 /* The format string */
472 | #define WSLUA_ARG_Struct_unpack_STRUCT2 2 /* The binary Lua string to unpack */
473 | #define WSLUA_OPTARG_Struct_unpack_BEGIN3 3 /* The position to begin reading from (default=1) */
474 | Header h;
475 | const char *fmt = wslua_checkstring_only(L, WSLUA_ARG_Struct_unpack_FORMAT1);
476 | size_t ld;
477 | const char *data = wslua_checklstring_only(L, WSLUA_ARG_Struct_unpack_STRUCT2, &ld);
478 | size_t pos = luaL_optinteger(L, WSLUA_OPTARG_Struct_unpack_BEGIN3, 1) - 1;
479 | defaultoptions(&h)((&h)->endian = native.endian, (&h)->align = 1, (&h)->noassign = 0);
480 | lua_settop(L, 2);
481 | while (*fmt) {
1 | Loop condition is true. Entering loop body | →
482 | int opt = *fmt++;
483 | size_t size = optsize(L, opt, &fmt);
484 | pos += gettoalign(pos, &h, opt, size);
2 | ← | Calling 'gettoalign' | →
5 | ← | Returning from 'gettoalign' | →
485 | luaL_argcheck(L, pos+size <= ld, 2, "data string too short")((void)((__builtin_expect(((pos+size <= ld) != 0), 1)) || luaL_argerror (L, (2), ("data string too short"))));
6 | ← | Assuming the condition is true | →
486 |
487 | if (opt == 'X') size = 0;
7 | ← | Assuming the condition is false | →
488 | if (h.noassign
7.1 | Field 'noassign' is false
&& size > 0) {
489 | /* if we're not assigning, and the opt type has a size, then loop again */
490 | /* this will not be the case for control options, 'c0', 's', and '=' */
491 | pos += size;
492 | continue;
493 | }
494 |
495 | luaL_checkstack(L, 1, "too many results");
496 | switch (opt) {
8 | ← | Control jumps to 'case 115:' at line 540 | →
497 | case 'b': case 'B': case 'h': case 'H':
498 | case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */
499 | int issigned = g_ascii_islower(opt)((g_ascii_table[(guchar) (opt)] & G_ASCII_LOWER) != 0);
500 | getinteger(L, data+pos, h.endian, issigned, (int)size);
501 | break;
502 | }
503 | case 'e': {
504 | Int64_unpack(L, data+pos, h.endian == LITTLE1);
505 | break;
506 | }
507 | case 'E': {
508 | UInt64_unpack(L, data+pos, h.endian == LITTLE1);
509 | break;
510 | }
511 | case 'x': case 'X': {
512 | break;
513 | }
514 | case 'f': {
515 | float f;
516 | memcpy(&f, data+pos, size);
517 | correctbytes((char *)&f, sizeof(f), h.endian);
518 | lua_pushnumber(L, f);
519 | break;
520 | }
521 | case 'd': {
522 | double d;
523 | memcpy(&d, data+pos, size);
524 | correctbytes((char *)&d, sizeof(d), h.endian);
525 | lua_pushnumber(L, d);
526 | break;
527 | }
528 | case 'c': {
529 | if (size == 0) {
530 | if (!lua_isnumber(L, -1))
531 | luaL_error(L, "format `c0' needs a previous size");
532 | size = wslua_touint32(L, -1)(uint32_t) ( lua_tointegerx(L,(-1),((void*)0)) );
533 | lua_pop(L, 1)lua_settop(L, -(1)-1);
534 | luaL_argcheck(L, pos+size <= ld, 2, "data string too short")((void)((__builtin_expect(((pos+size <= ld) != 0), 1)) || luaL_argerror (L, (2), ("data string too short"))));
535 | }
536 | if (!h.noassign)
537 | lua_pushlstring(L, data+pos, size);
538 | break;
539 | }
540 | case 's': {
541 | const char *e = (const char *)memchr(data+pos, '\0', ld - pos);
9 | ← | 'e' initialized here | →
542 | if (e == NULL((void*)0))
10 | ← | Assuming 'e' is equal to NULL | →
11 | ← | Taking true branch | →
543 | luaL_error(L, "unfinished string in data");
544 | size = (e - (data+pos)) + 1;
12 | ← | Subtraction of a null pointer (from variable 'e') and a non-null pointer results in undefined behavior
545 | if (!h.noassign)
546 | lua_pushlstring(L, data+pos, size - 1);
547 | break;
548 | }
549 | case '=': {
550 | lua_pushinteger(L, pos + 1);
551 | break;
552 | }
553 | default: controloptions(L, opt, &fmt, &h);
554 | }
555 | pos += size;
556 | }
557 | lua_pushinteger(L, pos + 1);
558 | WSLUA_RETURN(lua_gettop(L) - 2)return (lua_gettop(L) - 2); /* One or more values based on format, plus the position it stopped unpacking. */
559 | }
560 |
561 |
562 | WSLUA_CONSTRUCTORstatic int Struct_size (lua_State *L) {
563 | /* Returns the length of a binary string that would be consumed/handled by the given format string. */
564 | #define WSLUA_ARG_Struct_size_FORMAT1 1 /* The format string */
565 | Header h;
566 | const char *fmt = wslua_checkstring_only(L, WSLUA_ARG_Struct_size_FORMAT1);
567 | size_t pos = 0;
568 | defaultoptions(&h)((&h)->endian = native.endian, (&h)->align = 1, (&h)->noassign = 0);
569 | while (*fmt) {
570 | int opt = *fmt++;
571 | size_t size = optsize(L, opt, &fmt);
572 | pos += gettoalign(pos, &h, opt, size);
573 | if (opt == 's')
574 | luaL_argerror(L, 1, "option 's' has no fixed size");
575 | else if (opt == 'c' && size == 0)
576 | luaL_argerror(L, 1, "option 'c0' has no fixed size");
577 | if (!g_ascii_isalnum(opt)((g_ascii_table[(guchar) (opt)] & G_ASCII_ALNUM) != 0))
578 | controloptions(L, opt, &fmt, &h);
579 | pos += size;
580 | }
581 | lua_pushinteger(L, pos);
582 | WSLUA_RETURN(1)return (1); /* The size number */
583 | }
584 |
585 | WSLUA_CONSTRUCTORstatic int Struct_values (lua_State *L) {
586 | /* Returns the number of Lua values contained in the given format string.
587 | This will be the number of returned values from a call to Struct.unpack()
588 | not including the extra return value of offset position. (i.e., Struct.values()
589 | does not count that extra return value) This will also be the number of
590 | arguments Struct.pack() expects, not including the format string argument. */
591 | #define WSLUA_ARG_Struct_values_FORMAT1 1 /* The format string */
592 | Header h;
593 | const char *fmt = wslua_checkstring_only(L, WSLUA_ARG_Struct_values_FORMAT1);
594 | size_t vals = 0;
595 | defaultoptions(&h)((&h)->endian = native.endian, (&h)->align = 1, (&h)->noassign = 0);
596 | while (*fmt) {
597 | int opt = *fmt++;
598 | /* we use a size != 0 to mean it is a value */
599 | size_t size = optsize(L, opt, &fmt);
600 | /* but some will be zero and not be a value, or vice-versa */
601 | switch (opt) {
602 | case 's': case 'c':
603 | /* these are values */
604 | size = 1;
605 | break;
606 | case 'x': case 'X':
607 | /* these are not */
608 | size = 0;
609 | break;
610 | default:
611 | break;
612 | }
613 | if (!g_ascii_isalnum(opt)((g_ascii_table[(guchar) (opt)] & G_ASCII_ALNUM) != 0))
614 | controloptions(L, opt, &fmt, &h);
615 | else if (size && !h.noassign)
616 | vals++;
617 | }
618 | lua_pushinteger(L, vals);
619 | WSLUA_RETURN(1)return (1); /* The number of values */
620 | }
621 |
622 | WSLUA_CONSTRUCTORstatic int Struct_tohex (lua_State *L) {
623 | /* Converts the passed-in binary string to a hex-ascii string. */
624 | #define WSLUA_ARG_Struct_tohex_BYTESTRING1 1 /* A Lua string consisting of binary bytes */
625 | #define WSLUA_OPTARG_Struct_tohex_LOWERCASE2 2 /* True to use lower-case hex characters (default=false). */
626 | #define WSLUA_OPTARG_Struct_tohex_SEPARATOR3 3 /* A string separator to insert between hex bytes (default=nil). */
627 | const char* s = NULL((void*)0);
628 | size_t len = 0;
629 | bool_Bool lowercase = false0;
630 | const char* sep = NULL((void*)0);
631 |
632 | /* luaL_checklstring coerces the argument to a string, and that's ok for tohex,
633 | just not fromhex. In fact, we should accept/coerce a Int64/UInt64 here too someday. */
634 | s = luaL_checklstring(L, WSLUA_ARG_Struct_tohex_BYTESTRING1, &len);
635 |
636 | lowercase = wslua_optbool(L,WSLUA_OPTARG_Struct_tohex_LOWERCASE2,false0);
637 | sep = luaL_optstring(L,WSLUA_OPTARG_Struct_tohex_SEPARATOR,NULL)(luaL_optlstring(L, (3), (((void*)0)), ((void*)0)));
638 |
639 | wslua_bin2hex(L, (const uint8_t*)s, (unsigned)len, lowercase, sep);
640 | WSLUA_RETURN(1)return (1); /* The Lua hex-ascii string */
641 | }
642 |
643 | WSLUA_CONSTRUCTORstatic int Struct_fromhex (lua_State *L) {
644 | /* Converts the passed-in hex-ascii string to a binary string. */
645 | #define WSLUA_ARG_Struct_fromhex_HEXBYTES1 1 /* A string consisting of hexadecimal bytes like "00 B1 A2" or "1a2b3c4d" */
646 | #define WSLUA_OPTARG_Struct_fromhex_SEPARATOR2 2 /* A string separator between hex bytes/words (default none). */
647 | const char* s = NULL((void*)0);
648 | size_t len = 0;
649 | const char* sep = NULL((void*)0);
650 |
651 | /* luaL_checklstring coerces the argument to a string, and we don't want to do that */
652 | s = wslua_checklstring_only(L, WSLUA_ARG_Struct_fromhex_HEXBYTES1, &len);
653 |
654 | sep = luaL_optstring(L,WSLUA_OPTARG_Struct_fromhex_SEPARATOR,NULL)(luaL_optlstring(L, (2), (((void*)0)), ((void*)0)));
655 |
656 | wslua_hex2bin(L, s, (unsigned)len, sep);
657 | WSLUA_RETURN(1)return (1); /* The Lua binary string */
658 | }
659 |
660 | /* }====================================================== */
661 |
662 | /* Gets registered as metamethod automatically by WSLUA_REGISTER_CLASS/META */
663 | static int Struct__gc(lua_State* L _U___attribute__((unused))) {
664 | return 0;
665 | }
666 |
667 | WSLUA_METHODSstatic const luaL_Reg Struct_methods[] = {
668 | WSLUA_CLASS_FNREG(Struct,pack){ "pack", Struct_pack },
669 | WSLUA_CLASS_FNREG(Struct,unpack){ "unpack", Struct_unpack },
670 | WSLUA_CLASS_FNREG(Struct,size){ "size", Struct_size },
671 | WSLUA_CLASS_FNREG(Struct,values){ "values", Struct_values },
672 | WSLUA_CLASS_FNREG(Struct,tohex){ "tohex", Struct_tohex },
673 | WSLUA_CLASS_FNREG(Struct,fromhex){ "fromhex", Struct_fromhex },
674 | { NULL((void*)0), NULL((void*)0) }
675 | };
676 |
677 | WSLUA_METAstatic const luaL_Reg Struct_meta[] = {
678 | { NULL((void*)0), NULL((void*)0) }
679 | };
680 |
681 | LUALIB_APIextern int Struct_register(lua_State* L) {
682 | WSLUA_REGISTER_CLASS(Struct){ const wslua_class Struct_class = { .name = "Struct", .class_methods = Struct_methods, .class_meta = Struct_meta, .instance_methods = Struct_methods, .instance_meta = Struct_meta, .attrs = ((void *)0) }; wslua_register_class(L, &Struct_class); (lua_getfield (L, (-(2147483647/2 + 1000)), ("Struct"))); lua_pushcclosure( L, (Struct__gc), 0); lua_setfield(L, -2, "__gc"); lua_settop( L, -(1)-1); };
683 | return 0;
684 | }
685 |
686 | /*
687 | * Editor modelines - https://www.wireshark.org/tools/modelines.html
688 | *
689 | * Local Variables:
690 | * c-basic-offset: 2
691 | * tab-width: 8
692 | * indent-tabs-mode: nil
693 | * End:
694 | *
695 | * vi: set shiftwidth=2 tabstop=8 expandtab:
696 | * :indentSize=2:tabSize=8:noTabs=true:
697 | */