summaryrefslogtreecommitdiff
path: root/Debugging.m4
blob: da805b0201aa6f659a6700cd76b8b94971a03f1e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
TITLE(«

	All software sucks, be it open-source of proprietary. The only
	question is what can be done with particular instance of suckage,
	and that's where having the source matters.  -- Al Viro (2004)

», __file__)

SECTION(«Introduction»)

<p> It's safe to bet that every non-trivial program contains bugs.
Bugs in the operating system kernel are often fatal in that they
lead to a system crash which requires a reboot. However, thanks to
the concept of virtual memory, bugs in applications usually affect
neither the operating system nor independent processes which happen
to run at the same time. This makes user space programs easier to
debug than kernel code because the debugging tools will usually run
as a separate process. </p>

<p> We then look at <code>valgrind</code> and <code>gdb</code>, two popular tools
which help to locate bugs in application software. <code>valgrind</code>
is easy to use but is also limited because it does not alter the
target process. On the other hand, <code>gdb</code> is much more powerful
but also infamous for being hard to learn. The exercises aim to get
the reader started with both tools. </p>

<p> A couple of exercises on <code>gcc</code>, the GNU C compiler, ask the
reader to incorporate debugging information into an executable and
to see the effect of various diagnostic messages which show up when
valid but dubious code is being encountered. Warning messages can
be classified into one of two categories. First, there are
warnings which are based on static code analysis. These so-called
<em>compile-time</em> warnings are printed by the compiler when the
executable is being created from its source code. The second approach,
called <em>code instrumentation</em>, instructs the compiler to
add sanity checks to the executable, along with additional code that
prints a warning when one of these checks fails. In contrast to the
compile-time warnings, the messages of the second category show up at
<em>run-time</em>, and are generated by the compiled program rather
than by the compiler. </p>

EXERCISES()

<ul>
	<li> The <a href="#deref.c",<code>deref.c</code></a> program is
	flawed because <code>argv[1]</code> will be <code>NULL</code> if
	the program is invoked with no arguments. What is going to happen
	in this case? Compile the program (<code>cc deref.c -o deref</code>)
	and run it (<code>./deref</code>) to confirm. </li>

	<li> Run the <code>deref</code> program under strace (<code>strace
	deref</code>) and discuss the meaning of <code>si_addr</code> at the
	end of the output. </li>

	<li> Assume the buggy <code>deref</code> program is executed from the
	wrapper script <a href="#deref.sh"><code>deref.sh</code></a>. Explain
	why <code>strace deref.sh</code> does not show very useful
	information. Run <code>strace -f deref.sh</code> compare the output
	to the output of the first <code>strace</code> command where you ran
	the binary without the wrapper script. Discuss the implications of
	your findings with respect to debugging. </li>


	<li> "Prove" that the <a href="#strerror.c"><code>strerror.c</code></a>
	program works by compiling and running it, passing <code>1</code>
	as the only argument. Discuss in how far it is possible to prove
	correctness of a program by running it. </li>

	<li> Unfortunately, <code>strerror.c</code> has more flaws than
	lines. Point out as many as you can. </li>

	<li> Note that despite <code>strerror.c</code> code is full of bugs,
	the code compiles cleany. Discuss the reasons and the implications
	of this fact. </li>

	<li> Run <code>pinfo gcc</code> and read <code>Invoking GCC->Warning
	Options</code> to get an idea about the possible options to activate
	diagnostic messages. </li>

	<li> Recompile <code>strerror.c</code> with <code>-Wall</code> and
	explain all warnings shown. </li>

	<li> Run <code>valgrind strerror 1</code> and explain the message
	about the "conditional jump". </li>

	<li> Run <code>valgrind strerror</code> with no arguments and explain
	the part about the "invalid read". Why is it clear that this part
	refers to a <code>NULL</code> pointer dereference? </li>

	<li> Why is it a big difference if you run <code>strerror 2</code>
	instead of <code>strerror 1</code>? Run <code>valgrind strerror
	1</code> and <code>valgrind strerror 2</code> to confirm your
	answer. </li>

	<li> The <a href="#print_arg1.c"><code>print_arg1.c</code></a> program
	crashes due to <code>NULL</code> pointer dereference when it is called
	with no arguments. Compile the program and run it to confirm. </li>

	<li> Run <code>gdb print_arg1</code>. At the <code>(gdb</code>)
	prompt, execute the following commands and explain the output: </li>

	<ul>
		<li> <code>run</code></li>
		<li> <code>bt</code> </li>
	</ul>

	<li> Run <code>ls -l print_arg1; size print_arg1</code> and
	discuss the meaning of the <code>text</code>, <code>data</code>
	and <code>bss</code> columns in the output of the <code>size</code>
	command.  Compile the program again, this time with <code>-g</code>
	to add debugging information to the executable. Run <code>ls -l
	print_arg1; size print_arg1</code> again and discuss the difference,
	in particular the impact on performance due to the presence of the
	debugging information. </li>

	<li> Rerun the above <code>gdb</code> commands and note how the
	debugging information that has been stored in the executable makes
	the <code>bt</code> output much more useful. Discuss how debugging
	could be activated for third-party software for which the source code
	is available. </li>

	<li> Compile the program another two times with <code>-O0</code>
	and with <code>-O3</code> to optimize at different levels (where
	<code>-O0</code> means "don't optimize at all"). Rerun the above
	<code>gdb</code> commands on either excutable and note the difference
	regarding <code>print_it(</code>). </li>

	<li> Compile <a href="#ubsan.c"><code>ubsan.c</code></a> and run the
	program as follows: <code>./ubsan 123456 123456</code>. Explain why
	the the result is not the square of <code>123456</code>. Recompile
	it with <code>-fsanitize=undefined</code>. Then run the program again
	with the same options. </li>
</ul>

SUPPLEMENTS()

SUBSECTION(«deref.c»)

<pre>
	#include <stdio.h>
	#include <string.h>
	int main(int argc, char **argv)
	{
		printf("arg has %zu chars\n", strlen(argv[1]));
	}
</pre>

SUBSECTION(«deref.sh»)

<pre>
	#!/bin/sh
	./deref
</pre>

SUBSECTION(«strerror.c»)

<pre>
	#include "stdio.h"
	#include "stdlib.h"
	#include "string.h"
	#include "assert.h"

	/* print "system error: ", and the error string of a system call number */
	int main(int argc, char **argv)
	{
		unsigned errno, i;
		char *result = malloc(25); /* 5 * 5 */
		/* fail early on errors or if no option is given */
		if (errno && argc == 0)
			exit(0);
		errno = atoi(argv[1]);
		sprintf(result, strerror(errno));
		printf("system error %d: %s\n", errno, result, argc);
	}
</pre>

SUBSECTION(«print_arg1.c»)

<pre>
	#include <stdio.h>
	#include <stdlib.h>

	static int print_it(char *arg)
	{
		return printf("arg is %d\n", atoi(arg));
	}

	int main(int argc, char **argv)
	{
		return print_it(argv[1]);
	}
</pre>

SUBSECTION(«ubsan.c»)

<pre>
	#include <stdio.h>
	#include <stdlib.h>
	int main(int argc, char **argv)
	{
		int factor1 = atoi(argv[1]), factor2 = atoi(argv[2]),
			product = factor1 * factor2;
		return printf("%d * %d = %d\n", factor1, factor2, product);
	}
</pre>