Segmentation fault after pthread_cancel - c

In my program I have two threads, one calculates some recursive function and other displays "progress dots" as first threads does calculations. There is the code:
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#define test_errno(msg) do{if (errno) {perror(msg); exit(EXIT_FAILURE);}} while(0)
void *progressDotsThread(void* _arg){
for(int i=0;;i++){
printf(".");
fflush(stdout);
sleep(1);
}
}
void *seriesCalculateThread(void* n){
int result;
if((long)n==1) return (void*)(1);
else if((long)n==2) return (void*)(-5);
else{
int nmin1 = (long)seriesCalculateThread( (void*) ( (long)n -1 ));
int nmin2 = (long)seriesCalculateThread( (void*) ( (long)n -2 ));
result = nmin1*((long)n)*nmin2;
return (void*)(result);
}
}
int main(void) {
long n=0;
int result =0;
pthread_t w1,w2;
printf("Give n\n");
scanf ("%d",&n);
if(n<1){
printf("Value must be higher than 0");
}
else{
errno= pthread_create(&w2,NULL,seriesCalculateThread,(void *)n);
test_errno("pthread_create");
errno= pthread_create(&w1,NULL,progressDotsThread,NULL);
test_errno("pthread_create");
if(!pthread_join(w2,(void**)&result)){
errno = pthread_cancel(w1); //<--- Where segmentation fault happens
test_errno("pthread_cancel");
}
printf("%d\n", result);
}
return EXIT_SUCCESS;
}
I pthread_create both of them and then I if(!pthread_join) the one that does calculations and when calculations finish I cancel the one that display dots.
if(!pthread_join(w2,(void**)&result)){
errno = pthread_cancel(w1); //<--- Where segmentation fault happens
test_errno("pthread_cancel");
}
I do that although I get segmentation fault after pthread_cancel(w1) is called and I have no idea why that happens.
I tried compiling it terminal with gcc -g -Wall -pthread psthreadss.c -lpthread and it didn't work as well as in eclipse. I use Ubuntu 18.04 if that's revelant.

and then I if(!pthread_join) the one that does calculations and when calculations finish I cancel the one that display dots.
Your program has a logical bug, and a memory corruption bug.
The logical bug: you are assuming that !pthread_join means that calculations finished. It doesn't mean that. pthread_join returns non-zero status only if you invoked it with incorrect parameters (thread id w2 is not known, has not been started, or has already been joined).
Your code should instead look something like this:
int rc = pthread_join(w2, ...);
assert(rc == 0);
rc = pthread_cancel(w1);
assert (rc == 0);
On to the memory corruption bug: on a 64-bit system (which is what I assume you are using), sizeof(int) == sizeof(result) == 4, but sizeof(void*) == 8.
This code:
int result =0;
...
pthread_join(w1, (void**)&result)
takes an address of 4-byte result, and asks pthread_join to store an 8-byte value in there. Predictable outcome from this is stack corruption.
I don't see how exactly that triggers the SIGSEGV, but once you have undefined behavior in your program, all bets are off.
To fix this, change result type to intptr_t. I expect the crash to disappear after that fix.
You should also try to use Address Sanitizer: gcc -fsanitize=address -g ... before making any changes. There is a high chance it will tell you about stack overflow.

Related

Application exit with code -8 before even tries to divide by 0?

I have code like this:
#include <stdio.h>
#include <stdint.h>
int main() {
uint8_t result = 0;
uint8_t a = 0;
printf("Trying to divide by %d...\n", a);
result = (1/a != 0);
printf("%d\n", result);
return 0;
}
I have tried this in online compiler here: https://www.mycompiler.io/new/c
And this is the result:
[Execution complete with exit code -8]
I expected "Trying to divide by 0..." to be printed out, and then application should exit.
But the application exits before it prints anything.
Is it normal C behavior or some glitch of online compiler?
I can see two possible explanations here
First, it's important to know that undefined behavior can time travel back in time. There is no guarantee that the program will run fine until the problematic line. This may occur because the compiler might think that it would be better to reorder things like this:
uint8_t a = 0;
uint8_t result = (1/a != 0);
printf("Trying to divide by %d...\n", a);
The compiler is allowed to do this, because it's allowed to assume that division by zero will never happen.
Second, it could be the case that the output needs to be flushed. Whenever you want to make sure that a printout gets visible on screen before doing something else, use fflush(stdout). It might make a difference if you do this:
printf("Trying to divide by %d...\n", a);
fflush(stdout);
result = (1/a != 0);

How to exploit a buffer overflow to execute instructions on the stack

I'm starting to tinker with buffer overflows, and wrote the following program:
#include <unistd.h>
void g() {
execve("/bin/sh", NULL, NULL);
}
void f() {
long *return_address;
char instructions[] = "\xb8\x01\x00\x00\x00\xcd\x80"; // exit(1)
return_address = (long*) (&return_address + 2);
*return_address = (long)&g; // or (long)instructions
}
int main() {
f();
}
It does what I expect it to do : return_address overwrite the return address of f with the address of g, which opens a shell. However, if I set the return address to instructions, I got a segmentation fault, and none of the instructions in instructions is executed.
I compile with GCC, using -fno-stack-protector.
How could I prevent this segmentation fault occurring ?
At least one problem isn't related to the buffer overflow.
execve("/bin/sh", NULL, NULL);
That first NULL becomes the argv of the process you're starting. argv must be an array of strings that is terminated with a NULL. So a segfault may happen when /bin/sh starts up, tries to read argv[0], and dereferences NULL.
void g(void) {
char *argv[] = { "/bin/sh", NULL };
execve(argv[0], argv, NULL);
}
You might also add -z execstack to the gcc command line, which will tell the linker to permit an executable stack. You should also verify that the instructions you have there are what exit(1) compiles to on your system if you got them from a tutorial somewhere.

Getting Segmentation Fault in C when using sleep with Pthreads

I am creating a thread in C with PThreads which executes a function that is running in an infinit loop and prints some random json string every second into the console. At the beginning he prints the result of the function simulateLED with no problem, but after sleeping for 1 second, I'll get a Segmentation Fault (Core dumped). If I remove sleep, I'll not get it and the program works fine. Why do I get a Segmentation Fault with sleeping and how to fix it?
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
char *simulateLED() {
int temp;
int luftf;
char* jsonString;
time_t t;
srand((unsigned) time(&t));
int x=-10, y=50;
temp=(rand()%((y+1)-x))+x;
x=2, y=30;
luftf=(rand()%((y+1)-x))+x;
printf("%d %d\n", temp, luftf);
fflush(stdout);
sprintf(jsonString, "{\n\"TEMP\": %d,\n\"HUMI\": %d\n}", temp, luftf);
return jsonString;
}
void *simAndSendThread(void *param) {
while(1) {
printf("%s", simulateLED());
sleep(1);
}
}
int main(int argc, char *argv[]) {
pthread_t thread;
if(pthread_create(&thread, NULL, simAndSendThread, NULL)) {
fprintf(stderr, "Error creating thread\n");
return 1;
}
if(pthread_join(thread, NULL)) {
fprintf(stderr, "Error joining thread\n");
return 2;
}
pthread_exit(NULL);
return 0;
}
As #DavidSchwartz has pointed out, the reason for the Segmentation fault (core dumped) error is related to the jsonString pointer, that is currently not initialized (i.e., not pointing to anything). Hence, sprintf is writing to a random location, which might or might not work at times.
In order to fix it, you can statically assign space to the jsonString variable when you declare it, such as:
...
char jsonString[256];
...
This implies that you can have a string up to 255 characters (1 extra character reserved for \0). Alternatively, you can dynamically allocate the space using malloc:
...
char *jsonString = (char *)malloc(sizeof(char) * 256);
// Your code here
free(jsonString);
...
In this case, you must remember to release the allocation at the end of your function using free, otherwise you will leak memory. In case you haven't learned about dynamic memory yet, see When and why to use malloc?.
P.S.: If you are on Linux, I strongly recommend to use valgrind when you have memory-related errors. This tool will most probably hint where did you made the mistake. Check the Valgrind Quick Start Guide for more information.
You haven't allocated memory to jsonString and still trying to do sprintf and after return print
Try this
char* jsonString;
jsonString = malloc( 1024 );
And don't forget to free once done, you are using a while(1) and if you don't free there is every chance that you'll hit the out of memory error very soon.
If you enable full warnings you should have received a warning message for uninitialized variable for which eventually would have avoided all the crahes.

why am I getting runtime error?

I know that runtime error occurs when program consumes large memory or divide by 0 somewhere.
this is the code which prints all the numbers until user types 42.(does not print 42).
#include<stdio.h>
int main()
{
int n;
while(1)
{
scanf("%d",&n);
if(n==42)
break;
else
printf("%d",n);
}
}
please tell me why am I getting runtime error in such a simple code?
Your main function should return a int and you're not: that's why you get a Runtime Error. Here is the correct code:
#include <stdio.h>
#include <stdlib.h>
int main() {
int n = 0;
while(1) {
scanf("%d",&n);
if(n == 42)
break;
else
printf("%d",n);
}
return EXIT_SUCCESS;
}
You can also change return EXIT_SUCCESS; in return 0; if you don't want to include stdlib.h but here is why it's better.
You should also consider using another IDE than CodeChef. CodeBlocks or VisualStudio are better and more explicit with errors and warnings. And you should set you int n to 0 before using it.
This works perfectly fine as it is except when running on CodeChef.
According to the C standard (at least C99 and C11) a return 0 is implicit when main() ends without returning something. So even though it can be argued that it is a good idea to always have a return 0 at the end of main(), it is not wrong to skip it.
But that's not the case when running on CodeChef. For some reason they tread it as a run time error if main does not end with return 0.

How to check if malloc() overcommits memory

In my C program, based on the user's input, memory will be allocated for a given simulation. The initial problem I faced is that user can ask for a huge number to allocate but malloc() never fails until it runs out of memory then the program crashes.
I investigated the logic behind this and it now makes sense to me, see [1][2]. A possible workaround given here "SIGKILL while allocating memory in C++" suggests to set overcommit_memory in in /proc/sys/vm/overcommit_memory from 0 to 2.
This solved the problem from one side. But since I am using -fsanitize=address I get error from sanitizer.
Is there any better solution to this?
I guess the clang AddressSanitizer is failing because there is a legit leak. So my answer ignores that:
Alternatives:
Disable the overcommit behaviour, as you have already figured out: that is going to affect other processes and requires root.
run you app in a docker image with the oom killer disabled: that doesn't affect other processes but requires root to install docker (this is my favourite solution though).
write after malloc: may take long to alloc a huge chunk of memory and your process can still get killed because of other running process but doesn't require root.
use ulimit -v to limit the amount of memory depending on the machine: that also doesn't require root but your process might be killed anyway.
Code for the third alternative (for linux):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <setjmp.h>
jmp_buf resume_malloc;
void handle_malloc_error(int sig)
{
longjmp(resume_malloc, sig);
}
void *memalloc(size_t sz) {
void *p = 0;
int sig = setjmp(resume_malloc);
if ( sig == 0 ) {
p = malloc(sz);
signal(SIGSEGV, &handle_malloc_error);
memset(p, 0, sz);
} else {
p = 0;
}
signal(SIGSEGV, SIG_DFL);
return p;
}
int main(int argc, char *argv[])
{
size_t sz = 160L * 1024 * 1024 * 1024L;
void *p;
for (int i=0; i < 100; i++) {
printf("size: %lu\n", sz);
p = memalloc(sz);
if ( p == 0 ) {
printf("out of memory\n");
break;
}
sz *= 2;
}
}

Resources