Information about the application:
Linux - 2.4.1 Kernel
m68k based embedded application
Single process multithreaded application
We have an application where we have implemented the connection for the SIGSEGV with a segmentation_handler function. In this segmentation handler we create a file, do a file write (like "obtained stack frame"), then using backtrace and symbols write all the stack trace into the same file.
Problem: We get a SIGSEGV (confirmed due to creation of the log file) but unfortunately the file is empty (0kb file) with no information in it. (Even the first string which is a plain string is not available in the file).
I want to understand in what scenarios such a thing can happen because we can solve the crash if we get the stack trace, but we don't have it and the mechanism to get it did not work either :(
void segmentation_handler(int signal_no) {
char buffer[512]; .............
InitLog();//Create a log file
printf("\n*** segmentation fault occured ***\n");
fflush(stdout);
memset(buffer, 0, 512);
size = backtrace (array, 50);
strings = backtrace_symbols (array, size);
sprintf(buffer, "Obtained %d stack frames.\n", size);
Log(buffer);// Write the buffer into the file
for (n = 0; n < size; n++) {
sprintf(buffer, "%s\n", strings[n]); Log(buffer);
}
CloseLog();
}
Your segmentation handler is very naive and contains multiple errors. Here is a short list:
You are calling fprintf() and multiple other functions which are not async signal safe. Consider, fprintf uses a lock internally to synch multiple calls to the same file descriptor from multiple threads. What if your segmentation fault was in the middle of printf and the lock was taken? you would dead lock in the middle of the segmentation handlers...
You are allocating memory (call to backtrace_symbols), but if the segmentation fault was due to malloc arena corruption (a very likely cause of segmentation violations) you would double fault inside the segmentation handler.
If multiple threads cause an exception in the same time the code will open multiple times the file and run over the log.
There are other problems, but these are the basics...
There is a video on my lecture on how to write proper fault handlers available here: http://free-electrons.com/pub/video/2008/ols/ols2008-gilad-ben-yossef-fault-handlers.ogg
Remove the segmentation handler.
Allow the program to dump core (ulimit -c unlimited or setrlimit in process)
see if you have a core file.
do the backtrace thing offline using your toolchain debugger
You can also write a program that segfault on purpose, and test both method (ie post mortem using the core file, or in signal handler).
Related
Let us consider that I have an application that is to be executed on 1st node. This application however, cannot execute some function on this 1st node as the node lacks such capabilities. Hence, in order to make this application execution flawless, I am planning to steal the process's stack, heap & its registers using ptrace & send them over to other fully capable 2nd node. Here in this 2nd node, I would like to execute the same process(i.e same executable on the same architecture like x86) until the exact same point 1st process has exeuted, apply the previously stolen stack, heap & register's value onto this process and execute it here and transfer the results back to the 1st node and start executing the application from there.
I have also disabled the ASLR (Address space layout randomization) so that it will be one to one mapping between the process executed on remote node.
On applying such logic, the program ends up with "Stack smashing detected"
Is there anything that I am missing here, or is the idea itself not so feasible???
NOTE: I am also skipping the part of copying kernel stack, as the process on both sides are executed exactly until the same instruction. Please also note that this was a very simple program that I tried as I don't want the complexity of heaps to be involved.
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
void add_one(int *p){
*p += 2;
}
int main(int argc, char **argv)
{
int i = 0;
add_one(&i);
return 0;
}
Above picture holds that program that I experimented with, here I disassembled and found out the address of the function add_one, the point at which I would steal stack & process registers and send them over to apply onto the other identical process in node 2.
Any help on how to do such migrations and the things that I am missing would really help me in moving forward.
if you want to do this you need to at least disable stack canaries, because those will 100% mismatch when carrying over the execution to another machine even if you copied the entire address space.
-fno-stack-protector will do
I need some help about extend the signal sending functionality in kernel space in the presence of a SIGSEGV.
I have been working on OPTEE-OS (see the image at the bottom) and when a program, which is running on the RICH OS (in my case linux), crashes for some reason i need to forward the crash detected to the OPTEE Trustzone. I do not need to forward crash details or core dump file to the TrustZone but only that the process with pid XXXXXX crashed.
I know SIGSEGV is caught by the MMU causing an interrupt and that interrupt is handled by the kernel, which sends a SIGSEGV signal to the process. I want to extend the kernel module which sends the SIGSEGV signal in order to forward information to the TrustZone. But i do not know which kernel module in linux i could edit to do that.
For instance by starting with a simple .c code
void foo(int *p) {
*p = 1;
}
int main(int argc, char **argv) {
int *p = NULL;
foo(p);
}
which returns a SIGSEGV my question is:
Which kernel module should i edit in order to send the information to the Trustzone ? I think it is not a complex task because i just have to import the TruztZone library in the kernel module so as to do that .
OP-TEE ARCHITECTURE
#include<stdio.h>
int main()
{
char *name = "Vikram";
printf("%s",name);
name[1]='s';
printf("%s",name);
return 0;
}
There is no output printed on terminal and just get segmentation fault. But when I run it in GDB, I get following -
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400525 in main () at seg2.c:7
7 name[1]='s';
(gdb)
This means program receive SEG fault on 7th line (obviously I can't write on constant char array) . Then why printf() of line number 6 is not executed ?
This is due to stream buffering of stdout. Unless you do fflush(stdout) or you print a newline "\n" the output is may be buffered.
In this case, it's segfaulting before the buffer is flushed and printed.
You can try this instead:
printf("%s",name);
fflush(stdout); // Flush the stream.
name[1]='s'; // Segfault here (undefined behavior)
or:
printf("%s\n",name); // Flush the stream with '\n'
name[1]='s'; // Segfault here (undefined behavior)
First you should end your printfs with "\n" (or at least the last one). But that is not related to the segfault.
When the compiler compiles your code, it splits the binary into several section. Some are read only, while other are writeable.
Writing to an read only section may cause a segfault.
String literals are usually placed in a read only section (gcc should put it in ".rodata").
The pointer name points to that ro section. Therefore you must use
const char *name = "Vikram";
In my response I've used a few "may" "should". The behaviour depends on your OS, compiler and compilation settings (The linker script defines the sections).
Adding
-Wa,-ahlms=myfile.lst
to gcc's command line produces a file called myfile.lst with the generated assembler code.
At the top you can see
.section .rodata
.LC0:
.string "Vikram"
Which shows that the string is in Vikram.
The same code using (Must be in global scope, else gcc may store it on the stack, notice it is an array and not a pointer)
char name[] = "Vikram";
produces
.data
.type name, #object
.size name, 7
name:
.string "Vikram"
The syntax is a bit different but see how it is in .data section now, which is read-write.
By the way this example works.
The reason you are getting a segmentation fault is that C string literals are read only according to the C standard, and you are attempting to write 's' over the second element of the literal array "Vikram".
The reason you are getting no output is because your program is buffering its output and crashes before it has a chance to flush its buffer. The purpose of the stdio library, in addition to providing friendly formatting functions like printf(3), is to reduce the overhead of i/o operations by buffering data in in-memory buffers and only flushing output when necessary, and only performing input occasionally instead of constantly. Actual input and output will not, in the general case, occur at the moment when you call the stdio function, but only when the output buffer is full (or the input buffer is empty).
Things are slightly different if a FILE object has been set so it flushes constantly (like stderr), but in general, that's the gist.
If you're debugging, it is best to fprintf to stderr to assure that your debug printouts will get flushed before a crash.
By default when stdout is connected to a terminal, the stream is line-buffered. In practice, in your example the absence of '\n' (or of an explicit stream flush) is why you don't get the characters printed.
But in theory undefined behavior is not bounded (from the Standard "behavior [...] for which this International Standard imposes no requirements") and the segfault can happen even before the undefined behavior occurs, for example before the first printf call!
What happens if a CPU attempts to execute a binary that has been compiled with some instructions that your CPU doesn't support. I'm specifically wondering about some of the new AVX instructions running on older processors.
I'm assuming this can be tested for, and a friendly message could in theory be displayed to a user. Presumably most low level libraries will check this on your behalf. Assuming you didn't make this check, what would you expect to happen? What signal would your process receive?
A new instruction can be designed to be "legacy compatible" or it can not.
To the former class belong instructions like tzcnt or xacquire that have an encoding that produces valid instructions in older architecture: tzcnt is encoded as
rep bsf and xacquire is just repne.
The semantic is different of course.
To the second class belong the majority of new instructions, AVX being one popular example.
When the CPU encounters an invalid or reserved encoding it generates the #UD (for UnDefined) exception - that's interrupt number 6.
The Linux kernel set the IDT entry for #UD early in entry_64.S:
idtentry invalid_op do_invalid_op has_error_code=0
the entry points to do_invalid_op that is generated with a macro in traps.c:
DO_ERROR(X86_TRAP_UD, SIGILL, "invalid opcode", invalid_op)
the macro DO_ERROR generates a function that calls do_error_trap in the same file (here).
do_error_trap uses fill_trap_info (in the same file, here) to create a siginfo_t structure containing the Linux signal information:
case X86_TRAP_UD:
sicode = ILL_ILLOPN;
siaddr = uprobe_get_trap_addr(regs);
break;
from there the following calls happen:
do_trap in traps.c
force_sig_info in signal.c
specific_send_sig_info in signal.c
that ultimately culminates in calling the signal handler for SIGILL of the offending process.
The following program is a very simple example that generates an #UD
BITS 64
GLOBAL _start
SECTION .text
_start:
ud2
we can use strace to check the signal received by running that program
--- SIGILL {si_signo=SIGILL, si_code=ILL_ILLOPN, si_addr=0x400080} ---
+++ killed by SIGILL +++
as expected.
As Cody Gray commented, libraries don't usually rely on SIGILL, instead they use a CPU dispatcher or check the presence of an instruction explicitly.
Consider this example of a heap buffer overflow vulnerable program in Linux, taken directly from the "Buffer Overflow Attacks" (p. 248) book:
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
char *A, *B;
A = malloc(128);
B = malloc(32);
strcpy(A, argv[1]);
free(A);
free(B);
return 0;
}
Since unlink() has been changed to prevent the most simple form of exploit using the FD and BK pointers with a sanity check, I'm using a very old system I have with an old version of glibc (version 2.3.2). I'm also setting MALLOC_CHECK_=0 for this testing.
My goal of this toy example is to simply see if I can write 4 bytes to some arbitrary address I specify. The most simple test I can think of is to try write something to 0x41414141, which is an illegal address and should let the program crash to just confirm to me that it is indeed trying to write to this address (something I should be able to observe in GDB).
So I try executing with the argument perl -e 'print "A"x128 . "\xf8\xff\xff\xff" . "\xf8\xff\xff\xff" . "\x41\x41\x41\x41" . "\x41\x41\x41\x41" '
So I have:
Buffer A: 128 bytes of 0x41.
prev_size: 0xfffffff8
size: 0xfffffff8
FD: 0x41414141
BK: 0x41414141
I'm using 0xfffffff8 instead of 0xfffffffc because there is a note that with glibc 2.3 the third lowest bit NON_MAIN_AREA is used for management purposes for the arenas and has to be 0.
This should attempt to write 0x41414141 to 0x41414141 (+ 12 to be more correct, but still an illegal address), correct? However, when I execute this, the program simply terminates normally.
What am I missing here? This seems simple enough that it shouldn't be that hard to get to work.
I've tried various things such as using 0xfffffffc instead for prev_size and size, using legal addresses for FD (some address on the heap). I've tried swapping the order A and B are free()'d, I've tried to step into free() to see what happens in GDB but I got lost. Note that there shouldn't be any other security features on this system as it is very old and wouldn't have NX-bit, ASLR, etc (not that it should matter for the purpose of just writing 4 bytes to an illegal address).
Any ideas for how to make this work?
I could add that if using MALLOC_CHECK_=3 I get this:
malloc: using debugging hooks
malloc: using debugging hooks
free(): invalid pointer 0x8049688!
Program received signal SIGABRT, Aborted.
0x4004a1b1 in kill () from /lib/libc.so.6