Interruption instead of segfault with apparent memory violation

I came across this strange behavior when working with strings C. This is an exercise from the K & R book, where I had to write a function that adds one line to the end of another line. This obviously requires that the destination string have enough memory for the original string to match. Here is the code:

/* strcat: Copies contents of source at the end of dest */ char *strcat(char *dest, const char* source) { char *d = dest; // Move to the end of dest while (*dest != '\0') { dest++; } // *dest is now '\0' while (*source != '\0') { *dest++ = *source++; } *dest = '\0'; return d; } 

During testing, I wrote the following, expecting segfault to happen while the program was running:

 int main() { char s1[] = "hello"; char s2[] = "eheheheheheh"; printf("%s\n", strcat(s1, s2)); } 

As far as I understand, s1 gets an array of 6 chars allocated and s2 - an array of 13 chars . I thought that when strcat tries to write to s1 with indices above 6, the program will be segfault. Instead, everything works fine, but the program does not exit cleanly, but it does:

 helloeheheheheheh zsh: abort ./a.out 

and comes out with code 134, which I think just means interrupting.

Why don't I get segfault (or rewrite s2 if the lines are allocated on the stack)? Where are these lines in memory (stack or heap)?

Thanks for your help.

+4
source share
6 answers

I thought that when strcat tries to write to s1 with indices above 6 , the program will be segfault.

Writing outside the boundaries of memory allocated on the stack is an undefined behavior . Invoking this undefined behavior usually (but not always) results in segfault. However, you cannot be sure that segfault will happen.

The wikipedia link explains this pretty nicely:

When an instance of undefined behavior occurs, since the language specification affects anything, maybe nothing at all.

So, in this case you can get segfault, the program may abort, or sometimes it may just work fine. Or something. It is impossible to guarantee a result.

Where are these lines in memory (stack or heap)?

Since you declared them as char [] inside main() , these are arrays with automatic storage , which for practical purposes means that they are on the stack.

+7
source

Change 1:

I will try to explain how you can find the answer for yourself. I'm not sure what actually happens, as this is not a defined behavior (as others claim), but you can do a simple debugging to figure out what your compiler does.

Original answer

I guess both of them are on the stack. You can verify this by changing the code as follows:

 int main() { char c1 = 'X'; char s1[] = "hello"; char s2[] = "eheheheheheh"; char c2 = '3'; printf("%s\n", strcat(s1, s2)); } 

c1 and c2 will be on the stack. Knowing that you can check if s1 and s2 .

If the address c1 less than s1 , and the address s1 less than c2 , then it is on the stack. Otherwise, it is probably in your .bss section (which would be reasonable, but would break the recursion).

The reason I tackle the lines on the stack is because if you change them in a function, and this function calls itself, then the second call will not have its own copy of the lines and therefore will not be valid .. However, the compiler still knows that this function is not recursive and can put strings in .bss so that I can be wrong.

Assuming I'm assuming he's on the stack right in your code

 int main() { char s1[] = "hello"; char s2[] = "eheheheheheh"; printf("%s\n", strcat(s1, s2)); } 

"hello" (with a null terminator) is "eheheheheheh" stack, followed by "eheheheheheh" (with a null terminator).

They are both located one after the other (due to the simplicity of the order in which you wrote them), forming a single block of memory that you can write (but should not!) ... That's why there is no seg, you can see it, crashed before printf and looking at addresses.

s2 == (uintptr_t)s1 + (strlen(s1) + 1) should be right if I am right.

Change code with

 int main() { char s1[] = "hello"; char c = '3'; char s2[] = "eheheheheheh"; printf("%s\n", strcat(s1, s2)); } 

Must see c overwritten if I'm right ...

However, if I am mistaken and located in the .bss section, they can still be contiguous, and you will overwrite them without seg failing.

If you really want to know this, disassemble it:

Unfortunately, I only know how to do this on Linux. Try using nm <binary> > <text file>.txt or objdump -t <your_binary> > <text file>.sym to unload all the characters from your program. Teams must also provide a section where each character is located.

Find the file for the characters s1 and s2 , if you do not find them, this should mean that they are on the stack, but we will check this in the next step.

Use the objdump -S your_binary > text_file.S (make sure you have created your binary with debugging symbols), and then open the .S file in a text editor.

Find the characters s1 and s2 again (hope there are no others, I suspect not, but I'm not sure).

If you find their definitions followed by push or sub %esp , then they are on the stack. If you are not sure what their definitions mean, post it here and see.

+2
source

There is no seg error or even rewriting, as it can use second-line memory and still a function. Even give the correct answer. Cancellation is a sign that the program has realized that something is wrong. Try changing the order in which you declare the lines, and try again. It will probably not be so nice.

+1
source
 int main() { char s1[] = "hello"; char s2[] = "eheheheheheh"; printf("%s\n", strcat(s1, s2)); } 

use instead:

  int main() { char s1[20] = "hello"; char s2[] = "eheheheheheh"; printf("%s\n", strcat(s1, s2)); } 
0
source

This is why your program did not work:

Your strings are declared as array (s1 [] and s2 []). So they are on the stack. And only so happens that the memory for s2 [] immediately after s1 []. Therefore, when strcat () is called, all it does is move each character in s2 [] one byte forward. A stack as a stack is readable and writable. Thus, there is no limit to what you do.

But I believe that the compiler can find s1 [] and s2 [] where it sees fits, so this is just a fluke.

Now to make your program crash relatively easy

  • Translate s1 and s2 into your call: instead of strcat (s1, s2), execute strcat (s2, s1). This should lead to the elimination of stacking.
  • Change s1 [] and s2 [] to * s1 and * s2. This should call segfault when you write the readonly segment.
0
source

hmm .... the lines are on the stack on the right, since the heap is used only for dynamic memory allocation, etc.

segfault for invalid memory access, but with this array you just write stuff that crashes (out of bounds) for the array, so when writing I don't think you will have a problem. in C, it is actually left to the programmer to ensure that the bindings to arrays are safe.

Also, when reading, if you use pointers, I don’t think there will be a problem, as you can continue to read as long as you ever want and use the sum of the previous lengths. But if you use the functions mentioned in string.h, they relay by the presence of the null character "\ 0" to decide where to stop the operation - hence, I think your function worked!

but the ending may also indicate that any other variable / something that might be present next to the line layout may have been written using the char value .... Access to these could possibly have caused the program to exit !!

hope this helps .... good question by the way!

0
source

Source: https://habr.com/ru/post/1494610/


All Articles