tail recursion in gcc without return statement - c

Apparently the following code works in GCC. I tried that code in onlinegdb.
# include <stdio.h>
int calc_gcd (int a, int b) {
int r = a % b;
if (r == 0) {
return b;
} else {
calc_gcd (b, r);
}
}
int main() {
int a, b, gcd, dividend, divisor;
printf ("Enter two numbers: ");
scanf ("%d%d", &a, &b);
dividend = (a > b) ? a : b;
divisor = (a < b) ? a : b;
gcd = calc_gcd (dividend, divisor);
printf ("GCD = %d\n", gcd);
return 0;
}
But it fails in clang 13 with following results
tail_recursion_gcd.c:15:1: warning: non-void function does not return a value in all control paths [-Wreturn-type]
}
^
1 warning generated.
Enter two numbers: 15 10
GCD = 127 // garbage
I'm not getting it. Clearly what GCC allows isn't intuitive, you have to return from a function.
I've tried the following but that doesn't work in gcc
# include <stdio.h>
int useless_func()
{
3050;
}
int main() {
printf("result = %d", useless_func());
return 0;
}
The output is result = 0

TL;DR: The GCD function your teacher gave you is not portable.
What you want follows:
int calc_gcd (int a, int b) {
int r = a % b;
if (r == 0) {
return b;
} else {
return calc_gcd (b, r);
}
}
What version of GCC are you using, and on what operating system?
GCC is much more lenient than Clang. It looks like GCC decided that the function calc_gcd shouldn't fail and should return the result of calc_gcd(b, r).
When you compiled with Clang, I'm surprised that it didn't return 1. It did when I compiled on an M1 MacBook Pro.
From here:
Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning function.
On x86_64 Linux, when a function that returns a non-void value is called, space is allocated on the stack for the return value. Thus, with no explicit return statement, some value is still returned.
Note: on x86_64, the return value is stored in RAX (EAX is the lower bits).
I compiled with GCC 7.5.0 on x86_64 Ubuntu Server 18.04.
The command I used was gcc -o gcd gcd.c -no-pie -g.
Here's the assembly dump for calc_gcd:
0x00000000004005c7 <+0>: push rbp
0x00000000004005c8 <+1>: mov rbp,rsp
0x00000000004005cb <+4>: sub rsp,0x20
0x00000000004005cf <+8>: mov DWORD PTR [rbp-0x14],edi
0x00000000004005d2 <+11>: mov DWORD PTR [rbp-0x18],esi
0x00000000004005d5 <+14>: mov eax,DWORD PTR [rbp-0x14]
0x00000000004005d8 <+17>: cdq
0x00000000004005d9 <+18>: idiv DWORD PTR [rbp-0x18]
0x00000000004005dc <+21>: mov DWORD PTR [rbp-0x4],edx
0x00000000004005df <+24>: cmp DWORD PTR [rbp-0x4],0x0
0x00000000004005e3 <+28>: jne 0x4005ea <calc_gcd+35>
0x00000000004005e5 <+30>: mov eax,DWORD PTR [rbp-0x18]
0x00000000004005e8 <+33>: jmp 0x4005f9 <calc_gcd+50>
0x00000000004005ea <+35>: mov edx,DWORD PTR [rbp-0x4]
0x00000000004005ed <+38>: mov eax,DWORD PTR [rbp-0x18]
0x00000000004005f0 <+41>: mov esi,edx
0x00000000004005f2 <+43>: mov edi,eax
0x00000000004005f4 <+45>: call 0x4005c7 <calc_gcd>
0x00000000004005f9 <+50>: leave
0x00000000004005fa <+51>: ret
And with Clang 6.0.0:
0x0000000000400540 <+0>: push rbp
0x0000000000400541 <+1>: mov rbp,rsp
0x0000000000400544 <+4>: sub rsp,0x20
0x0000000000400548 <+8>: mov DWORD PTR [rbp-0x8],edi
0x000000000040054b <+11>: mov DWORD PTR [rbp-0xc],esi
0x000000000040054e <+14>: mov eax,DWORD PTR [rbp-0x8]
0x0000000000400551 <+17>: cdq
0x0000000000400552 <+18>: idiv DWORD PTR [rbp-0xc]
0x0000000000400555 <+21>: mov DWORD PTR [rbp-0x10],edx
0x0000000000400558 <+24>: cmp DWORD PTR [rbp-0x10],0x0
0x000000000040055c <+28>: jne 0x40056d <calc_gcd+45>
0x0000000000400562 <+34>: mov eax,DWORD PTR [rbp-0xc]
0x0000000000400565 <+37>: mov DWORD PTR [rbp-0x4],eax
0x0000000000400568 <+40>: jmp 0x40057b <calc_gcd+59>
0x000000000040056d <+45>: mov edi,DWORD PTR [rbp-0xc]
0x0000000000400570 <+48>: mov esi,DWORD PTR [rbp-0x10]
0x0000000000400573 <+51>: call 0x400540 <calc_gcd>
0x0000000000400578 <+56>: mov DWORD PTR [rbp-0x14],eax
0x000000000040057b <+59>: mov eax,DWORD PTR [rbp-0x4]
0x000000000040057e <+62>: add rsp,0x20
0x0000000000400582 <+66>: pop rbp
0x0000000000400583 <+67>: ret
You can see that the compilers made different choices in optimizations.
In fact, with GCC, rbp-0x18 is equal to 0x00000005 by +38.
With Clang, rbp-0x4 is equal to something entirely different. On my machine, in pwndbg, it was 0x00007fff.

Your code is bugged. It is allowed to return from an int function without returning a value only if the value will never be consumed. This is a holdover from K&R C that should no longer be used.
int calc_gcd (int a, int b) {
int r = a % b;
if (r == 0) {
return b;
} else {
calc_gcd (b, r);
}
}
Clearly incorrect. You want.
int calc_gcd (int a, int b) {
int r = a % b;
if (r == 0) {
return b;
} else {
return calc_gcd (b, r);
}
}
In fact this specific code tends to work at -O0 because the return value is left over left over in the register is the return value you want.

Related

Why printing an address changes their address?

Consider this code:
int a = 2;
int b = 5;
int c = 7;
When compiling it stores them in the order I assign them:
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 2 ; int a = 2
mov DWORD PTR [rbp-8], 5 ; int b = 5
mov DWORD PTR [rbp-12], 7 ; int c = 7
...
But if I decide to print the address of b, now b switched with c their addresses:
int a = 2;
int b = 5;
int c = 7;
printf("%d\n", &b);
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 2 ; int a = 2
mov DWORD PTR [rbp-12], 5 ; int b = 5
mov DWORD PTR [rbp-8], 7 ; int c = 7
...
So why do they switch addresses? By the way, I'm using gcc 12.2.
The Compiler Explorer uses gcc 12.2 and does not show this behaviour.
I tried compiling this code myself and gdb shows the "correct" order.
#include "stdio.h"
int main()
{
int a=2;
int b=5;
int c=7;
printf("%d", b);
return 0;
}
gdb output of "disassemble main":
Dump of assembler code for function main:
0x0000000000401126 <+0>: push rbp
0x0000000000401127 <+1>: mov rbp,rsp
0x000000000040112a <+4>: sub rsp,0x10
0x000000000040112e <+8>: mov DWORD PTR [rbp-0x4],0x2
0x0000000000401135 <+15>: mov DWORD PTR [rbp-0x8],0x5
0x000000000040113c <+22>: mov DWORD PTR [rbp-0xc],0x7
0x0000000000401143 <+29>: mov eax,DWORD PTR [rbp-0x8]
0x0000000000401146 <+32>: mov esi,eax
0x0000000000401148 <+34>: mov edi,0x402010
0x000000000040114d <+39>: mov eax,0x0
0x0000000000401152 <+44>: call 0x401030 <printf#plt>
0x0000000000401157 <+49>: mov eax,0x0
0x000000000040115c <+54>: leave
0x000000000040115d <+55>: ret
End of assembler dump.

Understanding Windows stack layout

I am following this buffer overflow tutorial: https://insecure.org/stf/smashstack.html
I want to make this program work in Windows
#include <stdio.h>
void f(int x, int y)
{
char buffer1[5];
char buffer2[10];
int *ret = buffer1 + 12;
(*ret) += 7;
}
int main()
{
int x;
x = 10;
f(1, 2);
x = 21;
printf("%d\n", x);
return 0;
}
This program attempts to modify the return address of function f so that this line x = 21; is ignored in main (ie. the program jumps directly to executing printf).
For some reason this trivial buffer overflow attack didn't work in Windows and I am not sure why.
This is how I understand the stack layout after the function f is called in x64 machines
high address
...
return address (4 bytes)
saved frame pointer (4 bytes)
1 (first argument for f; 4 bytes)
2 (second argument for f; 4 bytes)
buffer1 (1*8=8 bytes)
buffer2 (1*12=12 bytes)
...
low address
Using gdb, I get the following disassembly
Dump of assembler code for function main:
0x0000000000401590 <+0>: push %rbp
0x0000000000401591 <+1>: mov %rsp,%rbp
0x0000000000401594 <+4>: sub $0x30,%rsp
0x0000000000401598 <+8>: callq 0x401690 <__main>
0x000000000040159d <+13>: movl $0xa,-0x4(%rbp)
0x00000000004015a4 <+20>: mov $0x2,%edx
0x00000000004015a9 <+25>: mov $0x1,%ecx
0x00000000004015ae <+30>: callq 0x401560 <f>
0x00000000004015b3 <+35>: movl $0x15,-0x4(%rbp)
0x00000000004015ba <+42>: mov -0x4(%rbp),%eax
0x00000000004015bd <+45>: mov %eax,%edx
0x00000000004015bf <+47>: lea 0x2a3a(%rip),%rcx # 0x404000
0x00000000004015c6 <+54>: callq 0x402b70 <printf>
0x00000000004015cb <+59>: mov $0x0,%eax
0x00000000004015d0 <+64>: add $0x30,%rsp
0x00000000004015d4 <+68>: pop %rbp
0x00000000004015d5 <+69>: retq
End of assembler dump.
So in order to get return address in the stack, I must add 4+4+4=12 bytes. From the disassembly, in order to skip x = 21;, I must skip movl $0x15,-0x4(%rbp). So I need to add the additional 42-35=7 bytes to the return address.
Is there anything wrong with my understanding so far?

unexpected value when assign unsigned int(by negtive it) to signed long long [duplicate]

This question already has answers here:
Implicit type promotion rules
(4 answers)
Closed 1 year ago.
I happened run into a strange problem, see the code below:
#include <stdio.h>
int main() {
unsigned char a = 10;
unsigned int b = 10;
long long x = -a;
long long y = -b;
printf("x = %lld, y = %lld\n", x, y);
return 0;
}
Output:
x = -10, y = 4294967286
As you can see, when I assign -b to y (long long), it gives the wrong result, but the value of x is as expected.
After reading the asm code, I found that when assign negtive unsigned char to long long, the compiler generates a cdqe instruction(see line +25) to extend the sign to rax, while it doesn't do the same thing when unsigned int to long long.
I know the reason we got the value 4294967286 is that the high 32 bit of rax are all zero, rax = 0x00000000fffffff6.
So my question is why the compiler missing the cdqe instruction in the unsigned int case?
Dump of assembler code for function main:
0x000000000040052d <+0>: push rbp
0x000000000040052e <+1>: mov rbp,rsp
0x0000000000400531 <+4>: sub rsp,0x20
=> 0x0000000000400535 <+8>: mov BYTE PTR [rbp-0x1],0xa
0x0000000000400539 <+12>: mov DWORD PTR [rbp-0x8],0xa
0x0000000000400540 <+19>: movzx eax,BYTE PTR [rbp-0x1]
0x0000000000400544 <+23>: neg eax
0x0000000000400546 <+25>: cdqe
0x0000000000400548 <+27>: mov QWORD PTR [rbp-0x10],rax
0x000000000040054c <+31>: mov eax,DWORD PTR [rbp-0x8]
0x000000000040054f <+34>: neg eax
0x0000000000400551 <+36>: mov eax,eax
0x0000000000400553 <+38>: mov QWORD PTR [rbp-0x18],rax
0x0000000000400557 <+42>: mov rdx,QWORD PTR [rbp-0x18]
0x000000000040055b <+46>: mov rax,QWORD PTR [rbp-0x10]
0x000000000040055f <+50>: mov rsi,rax
0x0000000000400562 <+53>: mov edi,0x400610
0x0000000000400567 <+58>: mov eax,0x0
0x000000000040056c <+63>: call 0x400410 <printf#plt>
0x0000000000400571 <+68>: mov eax,0x0
0x0000000000400576 <+73>: leave
0x0000000000400577 <+74>: ret
Env:
OS: CentOS Linux release 7.8.2003 (Core), Linux 3.10.0-327.el7.x86_64 #1 SMP Thu Nov 19 22:10:57 UTC 2015 x86_64 x86_64 GNU/Linux
Gcc: gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39)
This has to do with implicit integer conversions.
The unsigned char a: Is first converted to an int and then negated. Result, -10.
The unsigned int b: Is not converted, so -b negates the unsigned int and you'll get UINT_MAX - 10 + 1.
If you cast to the target type first, -(long long)b, you'll get -10 there too.
Further reading: Implicit conversions

A few doubts on my C program to check prime [duplicate]

This question already has answers here:
Reaching end of function without return statement
(2 answers)
Closed 4 years ago.
This is a simple function to check whether the given number is prime or not and works for the most part except for 2 and 3 (as it doesn't enter the for loop) but they are already prime so no checking is required and the flag should remain untouched.But notice how I put the return statement inside the for loop(This was by mistake) so for integers 2 and 3 as they do not enter the loop the function should return 0(or so I assumed) but they always return 1.why? Is it because the program terminated incorrectly? then why always 1? it can be any non-zero integer right?and are there cases where the main() itself returns a 1?.Please clarify my doubts.Dev-C++ is the IDE used and it uses tdm-gcc 4.9.2 compiler.
int checkPrime(int n)
{
int i, isPrime = 0;
for(i = 2; i <= n/2; ++i) {
if(n % i == 0) {
isPrime = 1;
break;
}
return isPrime;
}
}
Your return statement is in wrong place. You should put it out of for loop. And also in programming 1 stands for true and 0 for false, so you are asking if n is prime and for 17 your function returns 0 (false), but it is prime:
int checkPrime(int n)
{
int i, isPrime = 1;
for(i = 2; i <= n/2; ++i) {
if(n % i == 0) {
isPrime = 0;
break;
}
}
return isPrime;
}
On x86 architecture, the return value of function is in %eax register.
Value present there would be regarded as the return value of the function checkPrime.
And in the %eax register, the value present would be '1', so '1' was treated as the return value.
Proof (Passing '2' to checkPrime() and at the end eax holds 1):-
Dump of assembler code for function checkPrime:
0x0000000000400526 <+0>: push %rbp
0x0000000000400527 <+1>: mov %rsp,%rbp
0x000000000040052a <+4>: mov %edi,-0x14(%rbp)
0x000000000040052d <+7>: movl $0x0,-0x8(%rbp)
0x0000000000400534 <+14>: movl $0x2,-0x4(%rbp)
0x000000000040053b <+21>: nop
0x000000000040053c <+22>: mov -0x14(%rbp),%eax
0x000000000040053f <+25>: mov %eax,%edx
0x0000000000400541 <+27>: shr $0x1f,%edx
0x0000000000400544 <+30>: add %edx,%eax
0x0000000000400546 <+32>: sar %eax
0x0000000000400548 <+34>: cmp -0x4(%rbp),%eax
0x000000000040054b <+37>: jl 0x400568 <checkPrime+66>
0x000000000040054d <+39>: mov -0x14(%rbp),%eax
0x0000000000400550 <+42>: cltd
0x0000000000400551 <+43>: idivl -0x4(%rbp)
0x0000000000400554 <+46>: mov %edx,%eax
0x0000000000400556 <+48>: test %eax,%eax
0x0000000000400558 <+50>: jne 0x400563 <checkPrime+61>
0x000000000040055a <+52>: movl $0x1,-0x8(%rbp)
0x0000000000400561 <+59>: jmp 0x400568 <checkPrime+66>
0x0000000000400563 <+61>: mov -0x8(%rbp),%eax
0x0000000000400566 <+64>: jmp 0x400568 <checkPrime+66>
0x0000000000400568 <+66>: pop %rbp
0x0000000000400569 <+67>: retq
(gdb) break *0x0000000000400569
Breakpoint 1 at 0x400569: file ./test.c, line 19.
(gdb) r
Starting program: /home/syed/Desktop/a.out
Breakpoint 1, 0x0000000000400569 in checkPrime (n=2) at ./test.c:19
19 }
(gdb) info registers eax
eax 0x1 1

GDB breakpoint in main() cannot access memory

My question is why when i set a breakpoint in main() with GDB i receive the error
<0xffffffffffffe550: Cannot access memory at address 0xffffffffffffe550>
I wanted to set a breakpoint in main() so i could examine the memory in the stack. My disassembled code is this:
0x00000000004008e8 <+0>: push %rbp
0x00000000004008e9 <+1>: mov %rsp,%rbp
0x00000000004008ec <+4>: add $0xffffffffffffff80,%rsp
0x00000000004008f0 <+8>: mov %edi,-0x74(%rbp)
0x00000000004008f3 <+11>: mov %rsi,-0x80(%rbp)
=> 0x00000000004008f7 <+15>: movl $0x1,-0x4(%rbp)
0x00000000004008fe <+22>: cmpl $0x1,-0x74(%rbp)
0x0000000000400902 <+26>: jle 0x400920 <main+56>
0x0000000000400904 <+28>: mov -0x80(%rbp),%rax
0x0000000000400908 <+32>: add $0x8,%rax
0x000000000040090c <+36>: mov (%rax),%rdx
0x000000000040090f <+39>: lea -0x70(%rbp),%rax
0x0000000000400913 <+43>: mov %rdx,%rsi
0x0000000000400916 <+46>: mov %rax,%rdi
0x0000000000400919 <+49>: callq 0x400670 <strcpy#plt>
0x000000000040091e <+54>: jmp 0x400924 <main+60>
0x0000000000400920 <+56>: movb $0x0,-0x70(%rbp)
0x0000000000400924 <+60>: callq 0x4006a0 <getuid#plt>
0x0000000000400929 <+65>: mov %eax,-0x8(%rbp)
0x000000000040092c <+68>: mov $0x0,%esi
0x0000000000400931 <+73>: mov $0x400c6e,%edi
0x0000000000400936 <+78>: mov $0x0,%eax
0x000000000040093b <+83>: callq 0x400720 <open#plt>
0x0000000000400940 <+88>: mov %eax,-0xc(%rbp)
0x0000000000400943 <+91>: cmpl $0xffffffff,-0xc(%rbp)
0x0000000000400947 <+95>: jne 0x40096b <main+131>
0x0000000000400949 <+97>: mov $0x400c80,%edi
0x000000000040094e <+102>: callq 0x400856 <fatal>
0x0000000000400953 <+107>: jmp 0x40096b <main+131>
0x0000000000400955 <+109>: lea -0x70(%rbp),%rdx
0x0000000000400959 <+113>: mov -0x8(%rbp),%ecx
0x000000000040095c <+116>: mov -0xc(%rbp),%eax
0x000000000040095f <+119>: mov %ecx,%esi
0x0000000000400961 <+121>: mov %eax,%edi
0x0000000000400963 <+123>: callq 0x40098c <print_notes>
0x0000000000400968 <+128>: mov %eax,-0x4(%rbp)
0x000000000040096b <+131>: cmpl $0x0,-0x4(%rbp)
0x000000000040096f <+135>: jne 0x400955 <main+109>
0x0000000000400971 <+137>: mov $0x400cb0,%edi
0x0000000000400976 <+142>: callq 0x400680 <puts#plt>
0x000000000040097b <+147>: mov -0xc(%rbp),%eax
0x000000000040097e <+150>: mov %eax,%edi
0x0000000000400980 <+152>: callq 0x4006e0 <close#plt>
0x0000000000400985 <+157>: mov $0x0,%eax
0x000000000040098a <+162>: leaveq
0x000000000040098b <+163>: retq
And the code until my main is this:
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "usefullfuncs.h"
#define FILENAME "/var/notes"
int print_notes(int, int, char *);
int find_user_note(int,int);
int search_note(char *, char *);
int main(int argc, char *argv[]){
int userid,printing=1,fd;
char searchstring[100];
if(argc>1)
strcpy(searchstring,argv[1]);
else
searchstring[0] = 0;
userid = getuid();
fd = open(FILENAME,O_RDONLY);
if(fd == -1)
fatal("in main() while opening file for reading");
while(printing)
printing = print_notes(fd,userid,searchstring);
printf("-------[ end of note data ]-------\n");
close(fd);
}
int print_notes(int fd,int uid,char *searchstring){
int note_lenght;
char note_buffer[100];
note_lenght = find_user_note(fd,uid);
if(note_lenght == -1)
return 0;
read(fd,note_buffer,note_lenght);
note_buffer[note_lenght] = 0;
if(search_note(note_buffer,searchstring))
printf(note_buffer);
return 1;
}
int find_user_note(int fd,int user_uid){
int note_uid=-1;
unsigned char byte;
int lenght;
while(note_uid != user_uid){
if(read(fd,&note_uid,4)!=4)
return -1;
if(read(fd,&byte,1)!=1)
return -1;
byte = lenght = 0;
while(byte != '\n'){
if(read(fd,&byte,1)!=1)
return -1;
lenght++;
}
}
lseek(fd,lenght*-1,SEEK_CUR);
printf("[DEBUG] found a %d byte note for user id %d\n",lenght,note_uid);
return lenght;
}
int search_note(char *note, char *keyword){
int i,keyword_lenght,match=0;
keyword_lenght = strlen(keyword);
if(keyword_lenght == 0)
return 1;
for(i=0;i < strlen(note);i++){
if(note[i] == keyword[match])
match++;
else{
if(note[i] == keyword[0])
match = 1;
else
match = 0;
}
if(match == keyword_lenght)
return 1;
}
return 0;
}
Thanks in advance.
(gdb) x/24s $esp
0xffffffffffffe550: <error: Cannot access memory at address 0xffffffffffffe550>
On an x86-64 target, $rsp should be used. Using $esp will lead to incorrect results.
$esp is taken from the bottom 32 bits of the 64-bit $rsp register, and gdb treats it as type int32_t. $rsp in your example was probably 0x7fffffffe550. Gdb's x command, which wants to use a 64-bit address, will take the bottom 32 bits of $rsp, 0xffffe550, and sign-extend that to 0xffffffffffffe550. That's almost certainly an invalid address; typical user-space addresses on Linux don't go above 0x7ffffffff000 or so.
Try x/24s $rsp. If you're trying to follow exercises out of an old book, you may be able to duplicate their 32-bit examples by giving gcc the -m32 option, if it supports it. Then you can use $esp.

Resources