2
#define _GNU_SOURCE #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> void func(); void main(int argc,char **argv) { printf("i am main\n"); int clone_flag,arg,retval; char *stack; clone_flag=CLONE_VM|CLONE_SIGHAND; stack=(char*)malloc(4096); retval=clone((void*)func,&(stack[4095]),clone_flag,NULL); stack=(char*)malloc(4096); retval=clone((void*)func,&(stack[4095]),clone_flag,NULL); } void func() { int i; for(i=0;i<3;i++) { printf("i: %d\n ",i); } } 

output:

i am main i: 0 i: 1 

strace -f

5915 fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(4, 1), ...}) = 0 5915 ioctl(1, TCGETS, {B38400 opost isig icanon echo ...}) = 0 5915 brk(NULL) = 0xaf2000 5915 brk(0xb14000) = 0xb14000 5915 write(1, "i am main\n", 10) = 10 5915 clone(child_stack=0xaf400f, flags=CLONE_VM|CLONE_SIGHAND) = 5916 5915 clone(child_stack=0xaf501f, flags=CLONE_VM|CLONE_SIGHAND) = 5917 5915 exit_group(5917) = ? 5915 +++ exited with 29 +++ 5917 write(1, "i", 1) = 1 5917 write(1, ": 0\n ", 5) = 5 5917 write(1, "i", 1) = 1 5917 write(1, ": 1\n ", 5) = 5 5916 --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0xaf194f} --- 5916 +++ killed by SIGSEGV (core dumped) +++ 5917 +++ killed by SIGSEGV +++ 

gdb ./a.out core

Core was generated by `./a.out'. Program terminated with signal SIGSEGV, Segmentation fault. #0 buffered_vfprintf (s=0x7f737fd43620 <_IO_2_1_stdout_>, format=0x40074e "i: %d\n ", args=0x1f49f27) at vfprintf.c:2299 2299 vfprintf.c: No such file or directory. [Current thread is 1 (LWP 6280)] (gdb) where #0 buffered_vfprintf (s=0x7f737fd43620 <_IO_2_1_stdout_>, format=0x40074e "i: %d\n ", args=0x1f49f27) at vfprintf.c:2299 #1 0x00007f737f9cb32d in _IO_vfprintf_internal ( s=0x7f737fd43620 <_IO_2_1_stdout_>, format=0x40074e "i: %d\n ", ap=ap@entry=0x1f49f27) at vfprintf.c:1293 #2 0x00007f737f9d3899 in __printf (format=<optimized out>) at printf.c:33 #3 0x00000000004006a8 in func () at code.c:23 #4 0x00007f737fa8541d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109 

it is confusing if I remove the"printf("i am main\n");" the code will run well

strace

5937 brk(NULL) = 0x1f75000 5937 brk(0x1f97000) = 0x1f97000 5937 clone(child_stack=0x1f75fff, flags=CLONE_VM|CLONE_SIGHAND) = 5938 5937 clone(child_stack=0x1f7700f, flags=CLONE_VM|CLONE_SIGHAND) = 5939 5937 exit_group(5939) = ? 5937 +++ exited with 51 +++ 5939 fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(4, 1), ...}) = 0 5939 ioctl(1, TCGETS, {B38400 opost isig icanon echo ...}) = 0 5939 write(1, "i: 0\n", 5) = 5 5939 write(1, " i: 1\n", 6) = 6 5939 write(1, " i: 2\n", 6) = 6 5938 write(1, " i: 2\ni: 0\n", 11) = 11 5939 exit_group(6) = ? 5939 +++ exited with 6 +++ 5938 write(1, " i: 1\n", 6) = 6 5938 write(1, " i: 2\n", 6) = 6 5938 exit_group(6) = ? 5938 +++ exited with 6 +++ 

why the "printf" makes such a big difference?(by the way,why the SIGSEGV killed another process?)

5
  • &(stack[4095]) is probably misaligned for use as a stack pointer. Try it with &(stack[4096]) instead. Commented Nov 6, 2019 at 14:39
  • 1
    Does this answer your question? segfault with clone() and printf Commented Nov 6, 2019 at 14:41
  • Are you sure you need clone ? Why not pthread_create or fork ? Commented Nov 6, 2019 at 14:43
  • Also : the signature should be int func(void*), not void func(). Commented Nov 6, 2019 at 14:46
  • ... the default stdio buffer size is 8192 bytes in my machine ... i had been making a stack less than 4096 ok before i started using printf ... unbuffering it at first had opposite effect that wanted, so then i setvbuf with buffer i allocated of 256 bytes with line buffering ( setvbuf ( stdout , buffer , _IOLBF , 128 ) ) and then all was ok with a small stack for each CLONE_VM and short printf lines ... Commented Dec 16, 2022 at 22:40

1 Answer 1

5

Your call to printf is overflowing the available stack. 4096 bytes isn't enough for all it needs to do. To confirm, here's a sample gdb session:

Reading symbols from ./a.out...done. (gdb) break 14 Breakpoint 1 at 0x400624: file source.c, line 14. (gdb) break 16 Breakpoint 2 at 0x400659: file source.c, line 16. (gdb) break func Breakpoint 3 at 0x40068b: file source.c, line 21. (gdb) run Starting program: a.out i am main Breakpoint 1, main (argc=1, argv=0x7fffffffe3a8) at source.c:14 14 retval=clone((void*)func,&(stack[4095]),clone_flag,NULL); (gdb) print/x stack $1 = 0x602420 (gdb) cont Continuing. [New LWP 25381] [Switching to LWP 25381] Thread 2 hit Breakpoint 3, func () at source.c:21 21 for(i=0;i<3;i++) (gdb) print/x &i $2 = 0x60340b (gdb) watch $rsp < 0x602420 Watchpoint 4: $rsp < 0x602420 

I've asked gdb to stop when the stack pointer ($rsp on x86-64 machines; you may need a different register according to your CPU) falls below the start of the allocated stack range.

(gdb) cont Continuing. [Switching to LWP 25375] Thread 1 "a.out" hit Breakpoint 2, main (argc=1, argv=0x7fffffffe3a8) at source.c:16 16 retval=clone((void*)func,&(stack[4095]),clone_flag,NULL); (gdb) print/x stack $3 = 0x603430 (gdb) cont Continuing. [New LWP 25383] [Switching to LWP 25383] Thread 3 hit Breakpoint 3, func () at source.c:21 21 for(i=0;i<3;i++) (gdb) cont Continuing. [Switching to LWP 25381] Thread 2 hit Watchpoint 4: $rsp < 0x602420 Old value = 0 New value = 1 buffered_vfprintf (s=0x7ffff7dd2620 <_IO_2_1_stdout_>, format=0x40074e "i: %d\n ", args=0x603327) at vfprintf.c:2295 2295 vfprintf.c: No such file or directory. (gdb) where #0 buffered_vfprintf (s=0x7ffff7dd2620 <_IO_2_1_stdout_>, format=0x40074e "i: %d\n ", args=0x603327) at vfprintf.c:2295 #1 0x00007ffff7a5a32d in _IO_vfprintf_internal (s=0x7ffff7dd2620 <_IO_2_1_stdout_>, format=0x40074e "i: %d\n ", ap=ap@entry=0x603327) at vfprintf.c:1293 #2 0x00007ffff7a62899 in __printf (format=<optimised out>) at printf.c:33 #3 0x00000000004006a8 in func () at source.c:23 #4 0x00007ffff7b1441d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109 

We can now see that the stack pointer has gone well below the stack range allocated.

(gdb) print/x $rsp $4 = 0x600c4f 

Regarding what difference the printf makes, you could compare execution traces (generated using a simple si loop). I found that they first diverge at this test:

 if (UNBUFFERED_P (s)) /* Use a helper function which will allocate a local temporary buffer for the stream and then call us again. */ return buffered_vfprintf (s, format, ap, mode_flags); 

It appears that the earlier call to printf has changed the mode of stdout to "unbuffered", so the later printf needs to go deeper to work with it. You could reproduce this by replacing the first printf with setbuf(stdout,NULL).

However, the real question isn't why printf makes a difference, it's "How Did That Ever Work?". There are three processes, two of which are being careless with the memory space they all share, so if and when they break is down to timing.

Sign up to request clarification or add additional context in comments.

2 Comments

It works when l enlarge the stack's size.But go back to the question,why does the child process's printf will in the stack range when the calling process don't printf?
When I tried without the early printf, I got inconsistent results -- it would still sometimes give incomplete output. So the early printf isn't what's causing the problem, it just makes it more likely.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.