0

I wanna modify the following function so that I can be able to skip the line : x='b' in the main function.

#include <stdio.h>

void function (int a, int b, int c) {
  int i, j;
  char buf[5];
  char buffer1[12];  
  int *ret; 
}

int main() {
  char x;
  x = 'a';
  printf ("Hello\n");
  printf ("I am going to skip a statement\n");  
  function(1,2,3);
  x = 'b';
  printf ("By");
  printf("The following value must be the a letter: %c\n",x);
}

I tried to get the return address and use the `ret' variable for adding the necessary bytes, but it seems that it's not correct.

ret=buffer1+12+4+1;
(*ret)+=4;


Anyway the x='b' is still executed.

sali333
  • 125
  • 4
  • 15
  • 5
    None of this makes any sense. I don't even understand what you are trying to do... are you somehow assuming that C instructions are equivalent to assembler instructions? – Lundin Jun 05 '19 at 07:59
  • What is your purpose? Compiler will handle it always properly, because it's it role to always generate code to correctly return from function. – s.paszko Jun 05 '19 at 07:59
  • It is btw impossible to manually set the PC in C, so you can't skip instructions by calculating a relative address. You have to use assembler for that. – Lundin Jun 05 '19 at 08:00
  • This is rather perverse insofar the language doesn't really support it. You could try `setjmp`, `longjmp`, inline assembly &c. &c. but that's "dodgy" C. – Bathsheba Jun 05 '19 at 08:00
  • Moreover the value of `x` can be stored in a register. You will have to make it `volatile`. –  Jun 05 '19 at 08:03
  • You can use an `if` statement, you can use a `goto` statement (if you must), or you can code the function in assembly. Trying to modify instruction addresses is going to be hit or miss and will be extremely unlikely to work (not to mention unstable). – Tom Karzes Jun 05 '19 at 08:03
  • 5
    For everyone's info: when a C function is called, the return address is pushed onto the stack (except for half a dozen reasons why it might not be). The OP's code attempts to modify that return address on the stack, so that the return address points to `printf("By");` instead of `x = 'b';`. There's at least a dozen reasons why this won't work, but it can be done if one understands all of the reasons why it might not. – user3386109 Jun 05 '19 at 08:09
  • Yes, this is what I am tying to do. To modify the return address so that it points in the `printf("By");` instead of `x = 'b';` – sali333 Jun 05 '19 at 08:14
  • The only way you're going to get this to work is to step through the assembly code with the debugger. That's the whole point of the exercise. I can't help you with that because what you see in the debugger depends on your hardware, the compiler that you're using, and the command line options that you used to compile. – user3386109 Jun 05 '19 at 08:21
  • since you specify gcc, have you looked at using the gcc extension for computed goto, is the outer function not in on it? – matt Jun 05 '19 at 09:16

4 Answers4

2

ret=buffer1+12+4+1;

This is Undefined Behaviour™, because your pointer arithmetic has left the allocation. The compiler is now free to do whatever it pleases, up to and including making daemons fly out of your nose.

(*ret)+=4;

And gcc has gotten really good at spotting out Undefined Behaviour, and pretty mean when to comes to choosing what it pleases.

If optimizations are enabled, it might simply ignore the statement altogether, if not, you probably have the offset wrong anyway, because there is absolutely no guarantee regarding layout of the variables. There will be some padding for alignment and even the order might be different. And I am not sure where you got the +1 from, but there is no reasonable layout where it would make sense.

You also seem to assume 32-bit target, but most Linuxes are full 64-bit these days.

Jan Hudec
  • 73,652
  • 13
  • 125
  • 172
1

At this moment in time, when using my very personal computer and there is a new moon at night, the code below works perfectly(note). It skips the 2nd puts instruction and jumps directly to the 3rd puts.

#include <stdio.h>
#include <setjmp.h>
#include <stdint.h>

#define can_you_see_what_I_mean() setjmp(jb)
#define might_as_well(task) ((uint32_t*)&jb)[20] = 0x401544; task(jb, 0)

static jmp_buf jb;

int main (void)
{
  puts("I get up");
  if(!can_you_see_what_I_mean())
  {
    might_as_well(longjmp);
  }
  puts("Go ahead and jump");
  puts("Hey you, who said that?");
}

Output:

I get up
Hey you, who said that?

(note) I wouldn't recommend anyone to use this code for any purpose, since it relies on extremely bad practice and contains countless forms of undefined behavior. It is extremely non-portable.

Lundin
  • 195,001
  • 40
  • 254
  • 396
0

This post can help you resolve your question with some caveats: The problem is that both working to edit a jmp_buf or writing inline assembly is not portable. I suggest to you to use one of the many control flow constructs (like if or a goto), with that being said take a look at this as a starting point. Implementing setjmp and longjmp in C without built in functions or assembly (getting incorrect return values)

  • 1
    Yeah, we shouldn't use very dirty words like goto, setjmp and longjmp. I for one find them offensive! – Lundin Jun 05 '19 at 08:54
  • Here you have it good sir, what about the rest? According to me that thread can really help him to do what he wants – Maxim Irrigad Jun 05 '19 at 08:56
0

the following proposed code:

  1. cleanly compiles
  2. depends on the statement in function() to skip the statement: x = 'b';

and now, the proposed code:

#include <stdio.h>

int function ( void ) 
{
    return 1;
}

int main( void ) 
{
  char x = 'a';
  printf ("Hello\n");
  printf ("I am going to skip a statement\n");  
  if( !function() )
  {
      x = 'b';
  }
  printf ("By");
  printf("The following value must be the a letter: %c\n",x);
}
user3629249
  • 16,402
  • 1
  • 16
  • 17