Re: core dump analysis, was Re: stack smashing detected

Finn Thain
2023-04-04T00:10:25Z

On Mon, 3 Apr 2023, Michael Schmitz wrote:

> On 2/04/23 22:46, Finn Thain wrote:
>
> > This is odd:
> >
> > https://sources.debian.org/src/dash/0.5.12-2/src/jobs.c/?hl=1165#L1165
> >
> > 1176 do {
> > 1177 gotsigchld = 0;
> > 1178 do
> > 1179 err = wait3(status, flags, NULL);
> > 1180 while (err < 0 && errno == EINTR);
> > 1181
> > 1182 if (err || (err = -!block))
> > 1183 break;
> > 1184
> > 1185 sigblockall(&oldmask);
> > 1186
> > 1187 while (!gotsigchld && !pending_sig)
> > 1188 sigsuspend(&oldmask);
> > 1189
> > 1190 sigclearmask();
> > 1191 } while (gotsigchld);
> > 1192
> > 1193 return err;
> >
> > Execution of dash under gdb doesn't seem to agree with the source code
> > above.
> >
> > If wait3() returns the child pid then the break should execute. And it
> > does return the pid (4107) but the while loop was not terminated. Hence
> > wait3() was called again and the same breakpoint was hit again. Also, the
>
> I wonder whether line 1182 got miscompiled by gcc. As err == 4107 it's >
> 0 and the break clearly ought to have been taken, and the second
> condition (which changes err) does not need to be examined. Do the same
> ordering constraints apply to '||' as to '&&' ?
>

AFAICT, the source code is valid. This article has some information:
https://stackoverflow.com/questions/628526/is-short-circuiting-logical-operators-mandated-and-evaluation-order

It looks like I messed up. waitproc() appears to have been invoked
twice, which is why wait3 was invoked twice...

GNU gdb (Debian 13.1-2) 13.1
...
(gdb) set osabi GNU/Linux
(gdb) file /bin/dash
Reading symbols from /bin/dash...
Reading symbols from /usr/lib/debug/.build-id/aa/4160f84f3eeee809c554cb9f3e1ef0686b8dcc.debug...
(gdb) b waitproc
Breakpoint 1 at 0xc346: file jobs.c, line 1168.
(gdb) b jobs.c:1180
Breakpoint 2 at 0xc390: file jobs.c, line 1180.
(gdb) run
Starting program: /usr/bin/dash
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/m68k-linux-gnu/libthread_db.so.1".
# x=$(:)
[Detaching after fork from child process 570]

Breakpoint 1, waitproc (status=0xeffff86a, block=1) at jobs.c:1168
1168 jobs.c: No such file or directory.
(gdb) c
Continuing.

Breakpoint 2, waitproc (status=0xeffff86a, block=1) at jobs.c:1180
1180 in jobs.c
(gdb) info locals
oldmask = {__val = {1997799424, 49154, 396623872, 184321, 3223896090, 53249,
3836788738, 1049411610, 867225601, 3094609920, 0, 1048580, 2857693183,
4184129547, 3435708442, 863764480, 184321, 3844141055, 4190425089,
4127248385, 3094659084, 597610497, 4135112705, 3844079616, 131072,
37355520, 184320, 3878473729, 3844132865, 3094663168, 3549089793,
3844132865}}
flags = 2
err = 570
oldmask = <optimized out>
flags = <optimized out>
err = <optimized out>
(gdb) c
Continuing.

Breakpoint 1, waitproc (status=0xeffff86a, block=0) at jobs.c:1168
1168 in jobs.c
(gdb) c
Continuing.

Breakpoint 2, waitproc (status=0xeffff86a, block=0) at jobs.c:1180
1180 in jobs.c
(gdb) info locals
oldmask = {__val = {1997799424, 49154, 396623872, 184321, 3223896090, 53249,
3836788738, 1049411610, 867225601, 3094609920, 0, 1048580, 2857693183,
4184129547, 3435708442, 863764480, 184321, 3844141055, 4190425089,
4127248385, 3094659084, 597610497, 4135112705, 3844079616, 131072,
37355520, 184320, 3878473729, 3844132865, 3094663168, 3549089793,
3844132865}}
flags = 3
err = -1
oldmask = <optimized out>
flags = <optimized out>
err = <optimized out>
(gdb) c
Continuing.
#

> What does the disassembly of this section look like?
>
> > while loop should have ended after the first iteration because gotsigchild
> > should have been set by the signal handler which executed before wait3()
> > even returned...
>
> Setting gotsigchild > 0 would cause the while loop to continue, no?
>

Right. Sorry for the noise.