### Bug Summary

File: | builds/wireshark/wireshark/epan/wslua/lrexlib/common.c
Warning: | line 181, column 22 Addition of a null pointer (via field 'arr') and a nonzero integer value (via field 'top') 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 common.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 /builds/wireshark/wireshark/epan -isystem /usr/include/lua5.5 -D CARES_NO_DEPRECATED -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D PCRE2_CODE_UNIT_WIDTH=8 -D REX_API=extern -D VERSION="2.9.2" -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-08-28-100404-3660-1 -x c /builds/wireshark/wireshark/epan/wslua/lrexlib/common.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 | /* common.c */
2 | /* See Copyright Notice in the file LICENSE */
3 | /* SPDX-License-Identifier: MIT */
4 |
5 | #include <wireshark.h>
6 | DIAG_OFF_CLANG(shorten-64-to-32)clang diagnostic push clang diagnostic ignored "-Wshorten-64-to-32"
7 | #ifdef _MSC_VER
8 | /* disable: " warning C4244: '=': conversion from 'lua _Integer' to 'int',
9 | * possible loss of data" */
10 | #pragma warning(disable:4244)
11 | /* warning C4267: '+=': conversion from 'size_t' to 'int',
12 | * possible loss of data */
13 | #pragma warning(disable:4267)
14 | #endif
15 |
16 | #include <stdlib.h>
17 | #include <ctype.h>
18 | #include <string.h>
19 | #include "lua.h"
20 | #include "lauxlib.h"
21 | #include "common.h"
22 |
23 | #define N_ALIGNsizeof(int) sizeof(int)
24 |
25 | /* the table must be on Lua stack top */
26 | int get_int_field (lua_State *L, const char* field)
27 | {
28 | int val;
29 | lua_getfield (L, -1, field);
30 | val = lua_tointeger (L, -1)lua_tointegerx(L,(-1),((void*)0));
31 | lua_pop (L, 1)lua_settop(L, -(1)-1);
32 | return val;
33 | }
34 |
35 | /* the table must be on Lua stack top */
36 | void set_int_field (lua_State *L, const char* field, int val)
37 | {
38 | lua_pushinteger (L, val);
39 | lua_setfield (L, -2, field);
40 | }
41 |
42 | void *Lmalloc(lua_State *L, size_t size) {
43 | void *ud;
44 | lua_Alloc lalloc = lua_getallocf(L, &ud);
45 | return lalloc(ud, NULL((void*)0), 0, size);
46 | }
47 |
48 | void *Lrealloc(lua_State *L, void *p, size_t osize, size_t nsize) {
49 | void *ud;
50 | lua_Alloc lalloc = lua_getallocf(L, &ud);
51 | return lalloc(ud, p, osize, nsize);
52 | }
53 |
54 | void Lfree(lua_State *L, void *p, size_t osize) {
55 | void *ud;
56 | lua_Alloc lalloc = lua_getallocf(L, &ud);
57 | lalloc(ud, p, osize, 0);
58 | }
59 |
60 | /* This function fills a table with string-number pairs.
61 | The table can be passed as the 1-st lua-function parameter,
62 | otherwise it is created. The return value is the filled table.
63 | */
64 | int get_flags (lua_State *L, const flag_pair **arrs) {
65 | const flag_pair *p;
66 | const flag_pair **pp;
67 | int nparams = lua_gettop(L);
68 |
69 | if(nparams == 0)
70 | lua_newtable(L)lua_createtable(L, 0, 0);
71 | else {
72 | if(!lua_istable(L, 1)(lua_type(L, (1)) == 5))
73 | luaL_argerror(L, 1, "not a table");
74 | if(nparams > 1)
75 | lua_pushvalue(L, 1);
76 | }
77 |
78 | for(pp=arrs; *pp; ++pp) {
79 | for(p=*pp; p->key; ++p) {
80 | lua_pushstring(L, p->key);
81 | lua_pushinteger(L, p->val);
82 | lua_rawset(L, -3);
83 | }
84 | }
85 | return 1;
86 | }
87 |
88 | const char *get_flag_key (const flag_pair *fp, int val) {
89 | for (; fp->key; ++fp) {
90 | if (fp->val == val)
91 | return fp->key;
92 | }
93 | return NULL((void*)0);
94 | }
95 |
96 | /* Classes */
97 |
98 | /*
99 | * class TFreeList
100 | * ***************
101 | * Simple array of pointers to TBuffer's.
102 | * The array has fixed capacity (not expanded automatically).
103 | */
104 |
105 | void freelist_init (TFreeList *fl) {
106 | fl->top = 0;
107 | }
108 |
109 | void freelist_add (TFreeList *fl, TBuffer *buf) {
110 | fl->list[fl->top++] = buf;
111 | }
112 |
113 | void freelist_free (TFreeList *fl) {
114 | while (fl->top > 0)
115 | buffer_free (fl->list[--fl->top]);
116 | }
117 |
118 | /*
119 | * class TBuffer
120 | * *************
121 | * Auto-extensible array of characters for building long strings incrementally.
122 | * * Differs from luaL_Buffer in that:
123 | * * its operations do not change Lua stack top position
124 | * * buffer_addvalue does not extract the value from Lua stack
125 | * * buffer_pushresult does not have to be the last operation
126 | * * Uses TFreeList class:
127 | * * for inserting itself into a TFreeList instance for future clean-up
128 | * * calls freelist_free prior to calling luaL_error.
129 | * * Has specialized "Z-operations" for maintaining mixed string/integer
130 | * array: bufferZ_addlstring, bufferZ_addnum and bufferZ_next.
131 | * * if the array is intended to be "mixed", then the methods
132 | * buffer_addlstring and buffer_addvalue must not be used
133 | * (the application will crash on bufferZ_next).
134 | * * conversely, if the array is not intended to be "mixed",
135 | * then the method bufferZ_next must not be used.
136 | */
137 |
138 | enum { ID_NUMBER, ID_STRING };
139 |
140 | void buffer_init (TBuffer *buf, size_t sz, lua_State *L, TFreeList *fl) {
141 | buf->arr = (char*) Lmalloc(L, sz);
142 | if (!buf->arr) {
143 | freelist_free (fl);
144 | luaL_error (L, "malloc failed");
145 | }
146 | buf->size = sz;
147 | buf->top = 0;
148 | buf->L = L;
149 | buf->freelist = fl;
150 | freelist_add (fl, buf);
151 | }
152 |
153 | void buffer_free (TBuffer *buf) {
154 | Lfree(buf->L, buf->arr, buf->size);
155 | }
156 |
157 | void buffer_clear (TBuffer *buf) {
158 | buf->top = 0;
159 | }
160 |
161 | void buffer_pushresult (TBuffer *buf) {
162 | lua_pushlstring (buf->L, buf->arr, buf->top);
163 | }
164 |
165 | void buffer_addbuffer (TBuffer *trg, TBuffer *src) {
166 | buffer_addlstring (trg, src->arr, src->top);
167 | }
168 |
169 | void buffer_addlstring (TBuffer *buf, const void *src, size_t sz) {
170 | size_t newtop = buf->top + sz;
171 | if (newtop > buf->size) {
9 | ← | Assuming 'newtop' is > field 'size' | →
10 | ← | Taking true branch | →
172 | char *p = (char*) Lrealloc (buf->L, buf->arr, buf->size, 2 * newtop); /* 2x expansion */
11 | ← | 'p' initialized here | →
173 | if (!p) {
12 | ← | Assuming 'p' is null | →
13 | ← | Taking true branch | →
174 | freelist_free (buf->freelist);
175 | luaL_error (buf->L, "realloc failed");
176 | }
177 | buf->arr = p;
14 | ← | Null pointer value stored to field 'arr' | →
178 | buf->size = 2 * newtop;
179 | }
180 | if (src
14.1 | 'src' is non-null
)
15 | ← | Taking true branch | →
181 | memcpy (buf->arr + buf->top, src, sz);
16 | ← | Addition of a null pointer (via field 'arr') and a nonzero integer value (via field 'top') results in undefined behavior
182 | buf->top = newtop;
183 | }
184 |
185 | void buffer_addvalue (TBuffer *buf, int stackpos) {
186 | size_t len;
187 | const char *p = lua_tolstring (buf->L, stackpos, &len);
188 | buffer_addlstring (buf, p, len);
189 | }
190 |
191 | void bufferZ_addlstring (TBuffer *buf, const void *src, size_t len) {
192 | int n;
193 | size_t header[2] = { ID_STRING };
194 | header[1] = len;
195 | buffer_addlstring (buf, header, sizeof (header));
196 | buffer_addlstring (buf, src, len);
8 | ← | Calling 'buffer_addlstring' | →
197 | n = len % N_ALIGNsizeof(int);
198 | if (n) buffer_addlstring (buf, NULL((void*)0), N_ALIGNsizeof(int) - n);
199 | }
200 |
201 | void bufferZ_addnum (TBuffer *buf, size_t num) {
202 | size_t header[2] = { ID_NUMBER };
203 | header[1] = num;
204 | buffer_addlstring (buf, header, sizeof (header));
205 | }
206 |
207 | /* 1. When called repeatedly on the same TBuffer, its existing data
208 | is discarded and overwritten by the new data.
209 | 2. The TBuffer's array is never shrunk by this function.
210 | */
211 | void bufferZ_putrepstring (TBuffer *BufRep, int reppos, int nsub) {
212 | char dbuf[] = { 0, 0 };
213 | size_t replen;
214 | const char *p = lua_tolstring (BufRep->L, reppos, &replen);
215 | const char *end = p + replen;
216 | BufRep->top = 0;
217 | while (p < end) {
1 | Loop condition is true. Entering loop body | →
218 | const char *q;
219 | for (q = p; q < end && *q != '%'; ++q)
2 | ← | Assuming 'q' is < 'end' | →
3 | ← | Assuming the condition is true | →
4 | ← | Loop condition is true. Entering loop body | →
5 | ← | Assuming 'q' is >= 'end' | →
220 | {}
221 | if (q
5.1 | 'q' is not equal to 'p'
!= p)
6 | ← | Taking true branch | →
222 | bufferZ_addlstring (BufRep, p, q - p);
7 | ← | Calling 'bufferZ_addlstring' | →
223 | if (q < end) {
224 | if (++q < end) { /* skip % */
225 | if (g_ascii_isdigit (*q)((g_ascii_table[(guchar) (*q)] & G_ASCII_DIGIT) != 0)) {
226 | int num;
227 | *dbuf = *q;
228 | num = strtol (dbuf, NULL((void*)0), 10);
229 | if (num == 1 && nsub == 0)
230 | num = 0;
231 | else if (num > nsub) {
232 | freelist_free (BufRep->freelist);
233 | luaL_error (BufRep->L, "invalid capture index");
234 | }
235 | bufferZ_addnum (BufRep, num);
236 | }
237 | else bufferZ_addlstring (BufRep, q, 1);
238 | }
239 | p = q + 1;
240 | }
241 | else break;
242 | }
243 | }
244 |
245 | /******************************************************************************
246 | The intended use of this function is as follows:
247 | size_t iter = 0;
248 | while (bufferZ_next (buf, &iter, &num, &str)) {
249 | if (str) do_something_with_string (str, num);
250 | else do_something_with_number (num);
251 | }
252 | *******************************************************************************
253 | */
254 | int bufferZ_next (TBuffer *buf, size_t *iter, size_t *num, const char **str) {
255 | if (*iter < buf->top) {
256 | size_t *ptr_header = (size_t*)(buf->arr + *iter);
257 | *num = ptr_header[1];
258 | *iter += 2 * sizeof (size_t);
259 | *str = NULL((void*)0);
260 | if (*ptr_header == ID_STRING) {
261 | int n;
262 | *str = buf->arr + *iter;
263 | *iter += *num;
264 | n = *iter % N_ALIGNsizeof(int);
265 | if (n) *iter += (N_ALIGNsizeof(int) - n);
266 | }
267 | return 1;
268 | }
269 | return 0;
270 | }
271 |
272 | #if LUA_VERSION_NUM(5 * 100 + 5) > 501
273 | int luaL_typerror (lua_State *L, int narg, const char *tname) {
274 | const char *msg = lua_pushfstring(L, "%s expected, got %s",
275 | tname, luaL_typename(L, narg)lua_typename(L, lua_type(L,(narg))));
276 | return luaL_argerror(L, narg, msg);
277 | }
278 | #endif
279 |
280 | #ifndef REX_NOEMBEDDEDTEST
281 | static int ud_topointer (lua_State *L) {
282 | lua_pushlightuserdata (L, lua_touserdata (L, 1));
283 | return 1;
284 | }
285 |
286 | static int ud_len (lua_State *L) {
287 | lua_pushinteger (L, lua_objlen (L, 1)lua_rawlen(L, (1)));
288 | return 1;
289 | }
290 |
291 | /* for testing purposes only */
292 | int newmembuffer (lua_State *L) {
293 | size_t len;
294 | const char* s = luaL_checklstring (L, 1, &len);
295 | void *ud = lua_newuserdata (L, len)lua_newuserdatauv(L,len,1);
296 | memcpy (ud, s, len);
297 | lua_newtable (L)lua_createtable(L, 0, 0); /* metatable */
298 | lua_pushvalue (L, -1);
299 | lua_setfield (L, -2, "__index"); /* metatable.__index = metatable */
300 | lua_pushcfunction (L, ud_topointer)lua_pushcclosure(L, (ud_topointer), 0);
301 | lua_setfield (L, -2, "topointer");
302 | lua_pushcfunction (L, ud_len)lua_pushcclosure(L, (ud_len), 0);
303 | lua_setfield (L, -2, "__len");
304 | lua_setmetatable (L, -2);
305 | return 1;
306 | }
307 | #endif /* #ifndef REX_NOEMBEDDEDTEST */