Bug Summary

File:src/lib/libmenu/m_driver.c
Warning:line 545, column 11
Access to field 'y' results in a dereference of a null pointer (loaded from variable 'item')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name m_driver.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/lib/libmenu/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/lib/libmenu -I /usr/src/lib/libmenu/../libcurses -D HAVE_CONFIG_H -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/lib/libmenu/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/lib/libmenu/m_driver.c
1/* $OpenBSD: m_driver.c,v 1.8 2010/01/12 23:22:07 nicm Exp $ */
2
3/****************************************************************************
4 * Copyright (c) 1998-2005,2008 Free Software Foundation, Inc. *
5 * *
6 * Permission is hereby granted, free of charge, to any person obtaining a *
7 * copy of this software and associated documentation files (the *
8 * "Software"), to deal in the Software without restriction, including *
9 * without limitation the rights to use, copy, modify, merge, publish, *
10 * distribute, distribute with modifications, sublicense, and/or sell *
11 * copies of the Software, and to permit persons to whom the Software is *
12 * furnished to do so, subject to the following conditions: *
13 * *
14 * The above copyright notice and this permission notice shall be included *
15 * in all copies or substantial portions of the Software. *
16 * *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
24 * *
25 * Except as contained in this notice, the name(s) of the above copyright *
26 * holders shall not be used in advertising or otherwise to promote the *
27 * sale, use or other dealings in this Software without prior written *
28 * authorization. *
29 ****************************************************************************/
30
31/****************************************************************************
32 * Author: Juergen Pfeifer, 1995,1997 *
33 ****************************************************************************/
34
35/***************************************************************************
36* Module m_driver *
37* Central dispatching routine *
38***************************************************************************/
39
40#include "menu.priv.h"
41
42MODULE_ID("$Id: m_driver.c,v 1.8 2010/01/12 23:22:07 nicm Exp $")
43
44/* Macros */
45
46/* Remove the last character from the match pattern buffer */
47#define Remove_Character_From_Pattern(menu)(menu)->pattern[--((menu)->pindex)] = '\0' \
48 (menu)->pattern[--((menu)->pindex)] = '\0'
49
50/* Add a new character to the match pattern buffer */
51#define Add_Character_To_Pattern(menu,ch){ (menu)->pattern[((menu)->pindex)++] = (ch); (menu)->
pattern[(menu)->pindex] = '\0'; }
\
52 { (menu)->pattern[((menu)->pindex)++] = (ch);\
53 (menu)->pattern[(menu)->pindex] = '\0'; }
54
55/*---------------------------------------------------------------------------
56| Facility : libnmenu
57| Function : static bool Is_Sub_String(
58| bool IgnoreCaseFlag,
59| const char *part,
60| const char *string)
61|
62| Description : Checks whether or not part is a substring of string.
63|
64| Return Values : TRUE - if it is a substring
65| FALSE - if it is not a substring
66+--------------------------------------------------------------------------*/
67static bool_Bool
68Is_Sub_String(
69 bool_Bool IgnoreCaseFlag,
70 const char *part,
71 const char *string
72)
73{
74 assert(part && string)((void)0);
75 if (IgnoreCaseFlag)
76 {
77 while (*string && *part)
78 {
79 if (toupper(UChar(*string++)((unsigned char)(*string++))) != toupper(UChar(*part)((unsigned char)(*part))))
80 break;
81 part++;
82 }
83 }
84 else
85 {
86 while (*string && *part)
87 if (*part != *string++)
88 break;
89 part++;
90 }
91 return ((*part) ? FALSE0 : TRUE1);
92}
93
94/*---------------------------------------------------------------------------
95| Facility : libnmenu
96| Function : int _nc_Match_Next_Character_In_Item_Name(
97| MENU *menu,
98| int ch,
99| ITEM **item)
100|
101| Description : This internal routine is called for a menu positioned
102| at an item with three different classes of characters:
103| - a printable character; the character is added to
104| the current pattern and the next item matching
105| this pattern is searched.
106| - NUL; the pattern stays as it is and the next item
107| matching the pattern is searched
108| - BS; the pattern stays as it is and the previous
109| item matching the pattern is searched
110|
111| The item parameter contains on call a pointer to
112| the item where the search starts. On return - if
113| a match was found - it contains a pointer to the
114| matching item.
115|
116| Return Values : E_OK - an item matching the pattern was found
117| E_NO_MATCH - nothing found
118+--------------------------------------------------------------------------*/
119NCURSES_EXPORT(int)int
120_nc_Match_Next_Character_In_Item_Name
121(MENU * menu, int ch, ITEM ** item)
122{
123 bool_Bool found = FALSE0, passed = FALSE0;
124 int idx, last;
125
126 T((T_CALLED("_nc_Match_Next_Character(%p,%d,%p)"), menu, ch, item));
127
128 assert(menu && item && *item)((void)0);
129 idx = (*item)->index;
130
131 if (ch && ch != BS(8))
132 {
133 /* if we become to long, we need no further checking : there can't be
134 a match ! */
135 if ((menu->pindex + 1) > menu->namelen)
136 RETURN(E_NO_MATCH)return( ((*__errno())=((-9))) );
137
138 Add_Character_To_Pattern(menu, ch){ (menu)->pattern[((menu)->pindex)++] = (ch); (menu)->
pattern[(menu)->pindex] = '\0'; }
;
139 /* we artificially position one item back, because in the do...while
140 loop we start with the next item. This means, that with a new
141 pattern search we always start the scan with the actual item. If
142 we do a NEXT_PATTERN oder PREV_PATTERN search, we start with the
143 one after or before the actual item. */
144 if (--idx < 0)
145 idx = menu->nitems - 1;
146 }
147
148 last = idx; /* this closes the cycle */
149
150 do
151 {
152 if (ch == BS(8))
153 { /* we have to go backward */
154 if (--idx < 0)
155 idx = menu->nitems - 1;
156 }
157 else
158 { /* otherwise we always go forward */
159 if (++idx >= menu->nitems)
160 idx = 0;
161 }
162 if (Is_Sub_String((bool_Bool)((menu->opt & O_IGNORECASE(0x08)) != 0),
163 menu->pattern,
164 menu->items[idx]->name.str)
165 )
166 found = TRUE1;
167 else
168 passed = TRUE1;
169 }
170 while (!found && (idx != last));
171
172 if (found)
173 {
174 if (!((idx == (*item)->index) && passed))
175 {
176 *item = menu->items[idx];
177 RETURN(E_OK)return( ((*__errno())=((0))) );
178 }
179 /* This point is reached, if we fully cycled through the item list
180 and the only match we found is the starting item. With a NEXT_PATTERN
181 or PREV_PATTERN scan this means, that there was no additional match.
182 If we searched with an expanded new pattern, we should never reach
183 this point, because if the expanded pattern matches also the actual
184 item we will find it in the first attempt (passed==FALSE) and we
185 will never cycle through the whole item array.
186 */
187 assert(ch == 0 || ch == BS)((void)0);
188 }
189 else
190 {
191 if (ch && ch != BS(8) && menu->pindex > 0)
192 {
193 /* if we had no match with a new pattern, we have to restore it */
194 Remove_Character_From_Pattern(menu)(menu)->pattern[--((menu)->pindex)] = '\0';
195 }
196 }
197 RETURN(E_NO_MATCH)return( ((*__errno())=((-9))) );
198}
199
200/*---------------------------------------------------------------------------
201| Facility : libnmenu
202| Function : int menu_driver(MENU *menu, int c)
203|
204| Description : Central dispatcher for the menu. Translates the logical
205| request 'c' into a menu action.
206|
207| Return Values : E_OK - success
208| E_BAD_ARGUMENT - invalid menu pointer
209| E_BAD_STATE - menu is in user hook routine
210| E_NOT_POSTED - menu is not posted
211+--------------------------------------------------------------------------*/
212NCURSES_EXPORT(int)int
213menu_driver(MENU * menu, int c)
214{
215#define NAVIGATE(dir)if (!item->dir) result = (-12); else item = item->dir \
216 if (!item->dir)\
217 result = E_REQUEST_DENIED(-12);\
218 else\
219 item = item->dir
220
221 int result = E_OK(0);
222 ITEM *item;
223 int my_top_row, rdiff;
224
225 T((T_CALLED("menu_driver(%p,%d)"), menu, c));
226
227 if (!menu)
1
Assuming 'menu' is non-null
2
Taking false branch
228 RETURN(E_BAD_ARGUMENT)return( ((*__errno())=((-2))) );
229
230 if (menu->status & _IN_DRIVER(0x02U))
3
Assuming the condition is false
4
Taking false branch
231 RETURN(E_BAD_STATE)return( ((*__errno())=((-5))) );
232 if (!(menu->status & _POSTED(0x01U)))
5
Assuming the condition is false
6
Taking false branch
233 RETURN(E_NOT_POSTED)return( ((*__errno())=((-7))) );
234
235 item = menu->curitem;
7
Value assigned to 'item'
236
237 my_top_row = menu->toprow;
238 assert(item)((void)0);
239
240 if ((c > KEY_MAX0777) && (c <= MAX_MENU_COMMAND(0777 + 17)))
8
Assuming 'c' is > KEY_MAX
9
Assuming the condition is true
10
Taking true branch
241 {
242 if (!((c == REQ_BACK_PATTERN(0777 + 15))
11
Assuming the condition is false
14
Taking true branch
243 || (c == REQ_NEXT_MATCH(0777 + 16)) || (c == REQ_PREV_MATCH(0777 + 17))))
12
Assuming the condition is false
13
Assuming the condition is false
244 {
245 assert(menu->pattern)((void)0);
246 Reset_Pattern(menu){ (menu)->pindex = 0; (menu)->pattern[0] = '\0'; };
247 }
248
249 switch (c)
15
Control jumps to 'case 519:' at line 313
250 {
251 case REQ_LEFT_ITEM(0777 + 1):
252 /*=================*/
253 NAVIGATE(left)if (!item->left) result = (-12); else item = item->left;
254 break;
255
256 case REQ_RIGHT_ITEM(0777 + 2):
257 /*==================*/
258 NAVIGATE(right)if (!item->right) result = (-12); else item = item->right;
259 break;
260
261 case REQ_UP_ITEM(0777 + 3):
262 /*===============*/
263 NAVIGATE(up)if (!item->up) result = (-12); else item = item->up;
264 break;
265
266 case REQ_DOWN_ITEM(0777 + 4):
267 /*=================*/
268 NAVIGATE(down)if (!item->down) result = (-12); else item = item->down;
269 break;
270
271 case REQ_SCR_ULINE(0777 + 5):
272 /*=================*/
273 if (my_top_row == 0 || !(item->up))
274 result = E_REQUEST_DENIED(-12);
275 else
276 {
277 --my_top_row;
278 item = item->up;
279 }
280 break;
281
282 case REQ_SCR_DLINE(0777 + 6):
283 /*=================*/
284 if ((my_top_row + menu->arows >= menu->rows) || !(item->down))
285 {
286 /* only if the menu has less items than rows, we can deny the
287 request. Otherwise the epilogue of this routine adjusts the
288 top row if necessary */
289 result = E_REQUEST_DENIED(-12);
290 }
291 else
292 {
293 my_top_row++;
294 item = item->down;
295 }
296 break;
297
298 case REQ_SCR_DPAGE(0777 + 7):
299 /*=================*/
300 rdiff = menu->rows - (menu->arows + my_top_row);
301 if (rdiff > menu->arows)
302 rdiff = menu->arows;
303 if (rdiff <= 0)
304 result = E_REQUEST_DENIED(-12);
305 else
306 {
307 my_top_row += rdiff;
308 while (rdiff-- > 0 && item != 0 && item->down != 0)
309 item = item->down;
310 }
311 break;
312
313 case REQ_SCR_UPAGE(0777 + 8):
314 /*=================*/
315 rdiff = (menu->arows < my_top_row) ? menu->arows : my_top_row;
16
Assuming 'my_top_row' is <= field 'arows'
17
'?' condition is false
316 if (rdiff <= 0)
18
Assuming 'rdiff' is > 0
19
Taking false branch
317 result = E_REQUEST_DENIED(-12);
318 else
319 {
320 my_top_row -= rdiff;
321 while (rdiff-- > 0 && item != 0 && item->up != 0)
20
Assuming 'item' is equal to null
322 item = item->up;
323 }
324 break;
21
Execution continues on line 541
325
326 case REQ_FIRST_ITEM(0777 + 9):
327 /*==================*/
328 item = menu->items[0];
329 break;
330
331 case REQ_LAST_ITEM(0777 + 10):
332 /*=================*/
333 item = menu->items[menu->nitems - 1];
334 break;
335
336 case REQ_NEXT_ITEM(0777 + 11):
337 /*=================*/
338 if ((item->index + 1) >= menu->nitems)
339 {
340 if (menu->opt & O_NONCYCLIC(0x20))
341 result = E_REQUEST_DENIED(-12);
342 else
343 item = menu->items[0];
344 }
345 else
346 item = menu->items[item->index + 1];
347 break;
348
349 case REQ_PREV_ITEM(0777 + 12):
350 /*=================*/
351 if (item->index <= 0)
352 {
353 if (menu->opt & O_NONCYCLIC(0x20))
354 result = E_REQUEST_DENIED(-12);
355 else
356 item = menu->items[menu->nitems - 1];
357 }
358 else
359 item = menu->items[item->index - 1];
360 break;
361
362 case REQ_TOGGLE_ITEM(0777 + 13):
363 /*===================*/
364 if (menu->opt & O_ONEVALUE(0x01))
365 {
366 result = E_REQUEST_DENIED(-12);
367 }
368 else
369 {
370 if (menu->curitem->opt & O_SELECTABLE(0x01))
371 {
372 menu->curitem->value = !menu->curitem->value;
373 Move_And_Post_Item(menu, menu->curitem){wmove((menu)->win,(menu)->spc_rows*(menu->curitem)->
y,((menu)->itemlen+(menu)->spc_cols)*(menu->curitem)
->x); _nc_Post_Item((menu),(menu->curitem));}
;
374 _nc_Show_Menu(menu);
375 }
376 else
377 result = E_NOT_SELECTABLE(-10);
378 }
379 break;
380
381 case REQ_CLEAR_PATTERN(0777 + 14):
382 /*=====================*/
383 /* already cleared in prologue */
384 break;
385
386 case REQ_BACK_PATTERN(0777 + 15):
387 /*====================*/
388 if (menu->pindex > 0)
389 {
390 assert(menu->pattern)((void)0);
391 Remove_Character_From_Pattern(menu)(menu)->pattern[--((menu)->pindex)] = '\0';
392 pos_menu_cursor(menu);
393 }
394 else
395 result = E_REQUEST_DENIED(-12);
396 break;
397
398 case REQ_NEXT_MATCH(0777 + 16):
399 /*==================*/
400 assert(menu->pattern)((void)0);
401 if (menu->pattern[0])
402 result = _nc_Match_Next_Character_In_Item_Name(menu, 0, &item);
403 else
404 {
405 if ((item->index + 1) < menu->nitems)
406 item = menu->items[item->index + 1];
407 else
408 {
409 if (menu->opt & O_NONCYCLIC(0x20))
410 result = E_REQUEST_DENIED(-12);
411 else
412 item = menu->items[0];
413 }
414 }
415 break;
416
417 case REQ_PREV_MATCH(0777 + 17):
418 /*==================*/
419 assert(menu->pattern)((void)0);
420 if (menu->pattern[0])
421 result = _nc_Match_Next_Character_In_Item_Name(menu, BS(8), &item);
422 else
423 {
424 if (item->index)
425 item = menu->items[item->index - 1];
426 else
427 {
428 if (menu->opt & O_NONCYCLIC(0x20))
429 result = E_REQUEST_DENIED(-12);
430 else
431 item = menu->items[menu->nitems - 1];
432 }
433 }
434 break;
435
436 default:
437 /*======*/
438 result = E_UNKNOWN_COMMAND(-8);
439 break;
440 }
441 }
442 else
443 { /* not a command */
444 if (!(c & ~((int)MAX_REGULAR_CHARACTER(0xff))) && isprint(UChar(c)((unsigned char)(c))))
445 result = _nc_Match_Next_Character_In_Item_Name(menu, c, &item);
446#ifdef NCURSES_MOUSE_VERSION1
447 else if (KEY_MOUSE0631 == c)
448 {
449 MEVENT event;
450 WINDOW *uwin = Get_Menu_UserWin(menu)((menu)->userwin ? (menu)->userwin : stdscr);
451
452 getmouse(&event);
453 if ((event.bstate & (BUTTON1_CLICKED((004L) << (((1) - 1) * 6)) |
454 BUTTON1_DOUBLE_CLICKED((010L) << (((1) - 1) * 6)) |
455 BUTTON1_TRIPLE_CLICKED((020L) << (((1) - 1) * 6))))
456 && wenclose(uwin, event.y, event.x))
457 { /* we react only if the click was in the userwin, that means
458 * inside the menu display area or at the decoration window.
459 */
460 WINDOW *sub = Get_Menu_Window(menu)((menu)->usersub ? (menu)->usersub : ((menu)->userwin
? (menu)->userwin : stdscr))
;
461 int ry = event.y, rx = event.x; /* screen coordinates */
462
463 result = E_REQUEST_DENIED(-12);
464 if (mouse_trafo(&ry, &rx, FALSE)wmouse_trafo(stdscr,&ry,&rx,0))
465 { /* rx, ry are now "curses" coordinates */
466 if (ry < sub->_begy)
467 { /* we clicked above the display region; this is
468 * interpreted as "scroll up" request
469 */
470 if (event.bstate & BUTTON1_CLICKED((004L) << (((1) - 1) * 6)))
471 result = menu_driver(menu, REQ_SCR_ULINE(0777 + 5));
472 else if (event.bstate & BUTTON1_DOUBLE_CLICKED((010L) << (((1) - 1) * 6)))
473 result = menu_driver(menu, REQ_SCR_UPAGE(0777 + 8));
474 else if (event.bstate & BUTTON1_TRIPLE_CLICKED((020L) << (((1) - 1) * 6)))
475 result = menu_driver(menu, REQ_FIRST_ITEM(0777 + 9));
476 RETURN(result)return( ((*__errno())=(result)) );
477 }
478 else if (ry > sub->_begy + sub->_maxy)
479 { /* we clicked below the display region; this is
480 * interpreted as "scroll down" request
481 */
482 if (event.bstate & BUTTON1_CLICKED((004L) << (((1) - 1) * 6)))
483 result = menu_driver(menu, REQ_SCR_DLINE(0777 + 6));
484 else if (event.bstate & BUTTON1_DOUBLE_CLICKED((010L) << (((1) - 1) * 6)))
485 result = menu_driver(menu, REQ_SCR_DPAGE(0777 + 7));
486 else if (event.bstate & BUTTON1_TRIPLE_CLICKED((020L) << (((1) - 1) * 6)))
487 result = menu_driver(menu, REQ_LAST_ITEM(0777 + 10));
488 RETURN(result)return( ((*__errno())=(result)) );
489 }
490 else if (wenclose(sub, event.y, event.x))
491 { /* Inside the area we try to find the hit item */
492 int i, x, y, err;
493
494 ry = event.y;
495 rx = event.x;
496 if (wmouse_trafo(sub, &ry, &rx, FALSE0))
497 {
498 for (i = 0; i < menu->nitems; i++)
499 {
500 err = _nc_menu_cursor_pos(menu, menu->items[i],
501 &y, &x);
502 if (E_OK(0) == err)
503 {
504 if ((ry == y) &&
505 (rx >= x) &&
506 (rx < x + menu->itemlen))
507 {
508 item = menu->items[i];
509 result = E_OK(0);
510 break;
511 }
512 }
513 }
514 if (E_OK(0) == result)
515 { /* We found an item, now we can handle the click.
516 * A single click just positions the menu cursor
517 * to the clicked item. A double click toggles
518 * the item.
519 */
520 if (event.bstate & BUTTON1_DOUBLE_CLICKED((010L) << (((1) - 1) * 6)))
521 {
522 _nc_New_TopRow_and_CurrentItem(menu,
523 my_top_row,
524 item);
525 menu_driver(menu, REQ_TOGGLE_ITEM(0777 + 13));
526 result = E_UNKNOWN_COMMAND(-8);
527 }
528 }
529 }
530 }
531 }
532 }
533 else
534 result = E_REQUEST_DENIED(-12);
535 }
536#endif /* NCURSES_MOUSE_VERSION */
537 else
538 result = E_UNKNOWN_COMMAND(-8);
539 }
540
541 if (E_OK(0) == result
21.1
'result' is equal to E_OK
)
22
Taking true branch
542 {
543 /* Adjust the top row if it turns out that the current item unfortunately
544 doesn't appear in the menu window */
545 if (item->y < my_top_row)
23
Access to field 'y' results in a dereference of a null pointer (loaded from variable 'item')
546 my_top_row = item->y;
547 else if (item->y >= (my_top_row + menu->arows))
548 my_top_row = item->y - menu->arows + 1;
549
550 _nc_New_TopRow_and_CurrentItem(menu, my_top_row, item);
551
552 }
553
554 RETURN(result)return( ((*__errno())=(result)) );
555}
556
557/* m_driver.c ends here */