Recall that in C, local variable declarations are interleaved with other statements, as shown in the example below.
#include <stdio.h>
#define print printf("doing something...\n")
int main(int argc, char **argv)
{
print;
int uninitted_int;
print;
int initted_int = 2;
print;
int *uninitted_intp;
print;
int *initted_intp = &initted_int;
print;
uninitted_int = 1;
print;
uninitted_intp = &uninitted_int;
print;
}
However, when a function is called and its stack frame is set up, all local variables are allocated immediately. They are, however, not but not initialised. They are initialised only when they are "initialised" in the C source, as shown in the example below.
(.venv) ‹[para]› ~/Code/structs.sh/debugger = gcc src/debugger/test/test_uninit.c -g -o a
(.venv) ‹[para]› ~/Code/structs.sh/debugger = gdb -q a
Reading symbols from a...
(gdb) start
---<snip>---
7 print;
(gdb) info locals
uninitted_int = 65535
initted_int = -136153344
uninitted_intp = 0xffffffffeb98
initted_intp = 0x20
(gdb) n
doing something...
9 print;
(gdb) n
doing something...
10 int initted_int = 2;
(gdb) n
11 print;
(gdb) info locals
uninitted_int = 65535
initted_int = 2
uninitted_intp = 0xffffffffeb98
initted_intp = 0x20
(gdb)
The core issue is that the debugger will preemptively list all local vars. Most of which will be uninitialised and that is problematic since (1) uninitialised data can confuse the end users (students) when they see junk values like -136153344 and (2) uninitialised pointers will confuse debugger.trace() into trying to find, say, the next linked list node at some garbage memory location like 0x20.
Your task is to see that all local variables are preemptively zero-initialised immediately after a function call.
Hint 1
- You can use
-stack-list-variables --thread 1 --frame 0 --all-values to find variables from the topmost frame. It returns both arguments and locals, so you must filter out objects that contain arg="1".
- You can use
-var-create <name> * <var> to create a GDB variable bound to a C variable.
- You can then use
-var-assign <name> <value> to assign it a value (for this issue, assign it zero)
- Don't forget to
-var-delete <name> for posterity
- Refer to
debugger.var_details() for how to generate and manage variable names
Example:
(.venv) ‹[para]› ~/Code/structs.sh/debugger = gdb -q a --interpreter=mi4
---<snip>---
(gdb)
start
---<snip>---
(gdb)
---<snip>---
(gdb)
-stack-list-variables --thread 1 --frame 0 --all-values
^done,variables=[{name="argc",arg="1",value="1"},{name="argv",arg="1",value="0xffffffffeb98"},{name="uninitted_int",value="65535"},{name="initted_int",value="-136153344"},{name="uninitted_intp",value="0xffffffffeb98"},{name="initted_intp",value="0x20"}]
(gdb)
-var-create x * uninitted_int
^done,name="x",numchild="0",value="65535",type="int",thread-id="1",has_more="0"
(gdb)
-var-assign x 0
^done,value="0"
(gdb)
-var-delete x
^done,ndeleted="1"
(gdb)
-stack-list-variables --thread 1 --frame 0 --all-values
^done,variables=[{name="argc",arg="1",value="1"},{name="argv",arg="1",value="0xffffffffeb98"},{name="uninitted_int",value="0"},{name="initted_int",value="-136153344"},{name="uninitted_intp",value="0xffffffffeb98"},{name="initted_intp",value="0x20"}]
(gdb)
---<snip>---
Hint 2
- You can detect when you've entered a function by listening to asynchronous exec
(*) outputs
- Refer to
src/demo.py on how to use @debug.on_oob to catch them
Example:
(.venv) ‹[para]› ~/Code/structs.sh/debugger = gdb -q a --interpreter=mi4
---<snip>---
(gdb)
-break-insert main
---<snip>---
(gdb)
-exec-run
---<snip>---
(gdb)
---<snip>---
*stopped,reason="breakpoint-hit",disp="keep",bkptno="1",frame={addr="0x0000aaaaaaaa0764",func="main",args=[{name="argc",value="1"},{name="argv",value="0xffffffffeb98"}],file="src/debugger/test/test_uninit.c",fullname="/home/tosha/Code/structs.sh/debugger/src/debugger/test/test_uninit.c",line="7",arch="aarch64"},thread-id="1",stopped-threads="all",core="0"
(gdb)
Recall that in C, local variable declarations are interleaved with other statements, as shown in the example below.
However, when a function is called and its stack frame is set up, all local variables are allocated immediately. They are, however, not but not initialised. They are initialised only when they are "initialised" in the C source, as shown in the example below.
The core issue is that the debugger will preemptively list all local vars. Most of which will be uninitialised and that is problematic since (1) uninitialised data can confuse the end users (students) when they see junk values like
-136153344and (2) uninitialised pointers will confusedebugger.trace()into trying to find, say, the next linked list node at some garbage memory location like0x20.Your task is to see that all local variables are preemptively zero-initialised immediately after a function call.
Hint 1
-stack-list-variables --thread 1 --frame 0 --all-valuesto find variables from the topmost frame. It returns both arguments and locals, so you must filter out objects that containarg="1".-var-create <name> * <var>to create a GDB variable bound to a C variable.-var-assign <name> <value>to assign it a value (for this issue, assign it zero)-var-delete <name>for posteritydebugger.var_details()for how to generate and manage variable namesExample:
Hint 2
(*)outputssrc/demo.pyon how to use@debug.on_oobto catch themExample: