Segmentation fault when using recursive sum operation - c

I'm having an issue using two different recursive sum methods, where at some point both fail and return a segmentation fault error. The first, sum_a only fails on high values, passed 260000, which i do not know why, and the second sum_b always fails. Any help would be appreciated.
Thanks
The execution is done as follows : ./sum a x
Where x is the number of recursions wanted for the SUM(1:x) and a is either a or b
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned count=0;
void signal_segv_handler(int sig) {
char s[50];
sprintf(s, "Segmentation fault! count=%u\n", count);
write(2, s, strlen(s));
_exit(1);
}
unsigned long long *sum_b(unsigned long long x) {
unsigned long long *s;
count++;
if (x>0)
*s = *sum_b(x - 1) + x;
else
*s = 0;
return s;
}
unsigned long long sum_a(unsigned long long x) {
count++;
if (x>0)
return sum_a(x - 1) + x;
else
return 0;
}
int main(int argc, char** argv) {
unsigned long long x;
unsigned long long *sum_result;
char result[100];
static char stack[SIGSTKSZ];
stack_t ss = {
.ss_size = SIGSTKSZ,
.ss_sp = stack,
};
struct sigaction sa = {
.sa_handler = signal_segv_handler,
.sa_flags = SA_ONSTACK
};
sigaltstack(&ss, 0);
sigfillset(&sa.sa_mask);
sigaction(SIGSEGV, &sa, 0);
if (argc < 3) {
printf("Please specify the sum function to use (a or b) and the target number of integers to sum.\n");
return -1;
}
x = atoi(argv[2]);
if (strcmp(argv[1], "a") == 0)
sprintf(result, "sum_a = %llu for x=%llu, count=%u \n", sum_a(x), x, count);
else if (strcmp(argv[1], "b") == 0) {
sum_result = sum_b(x);
sprintf(result, "sum_b = %llu for x=%llu, count=%u \n", sum_result, x, count);
free(sum_result);
}
else {
printf("error: function must be a or b\n");
return -1;
}
write(1, result, strlen(result));
return 0;
}

unsigned long long *sum_b(unsigned long long x) {
unsigned long long *s; <------
count++;
if (x>0)
*s = *sum_b(x - 1) + x;
else
*s = 0;
return s;
}
You are using an uninitialized pointer without any mem allocation to store the sum results.

Both sum_a and sum_b cause stack overflow when their iterations consume more memory than available in the stack. Seems like the stack size for your program is 8192KB(you may check it in the console with ulimit -s), one sum_a iteration consumes 32 bytes, and sum_b needs 48 bytes(because of more variables than in sum_a - unsigned long long *s;, and probably 16 byte stack aligning I'm not sure), so they owerflow the stack on ~260k and ~170k iteration respectively.
But sum_b has another point of segfault, here *s = *sum_b(x - 1) + x; and here *s = 0;
this is assigning to an uninitialized pointer (wild pointer, which points to a random memory address). It happens if no stack overflow took place of course(in that case assignment operation in this lines is never reached).
Some remarks not relevant to the question:
You allocate alt stack on the main stack, if which is overflown, your handler is in a bad stance, it still may work in some situations, but you should change static char stack[SIGSTKSZ]; to malloc function.
You should avoid using not signal-safe functions in the signal handler, the list of functions you may find in the signal-safety man.
This line is probably not necessary, you may remove it sigfillset(&sa.sa_mask);
Another segfault: free(sum_result); You should always balance a malloc with a call to free.

Related

Segmentation Fault with pointers arrays

I have a Segmentation fault according to my debugger but I don't know why.
Here is the code involved. Did I miss something ?
typedef struct Process Process;
struct Process {
unsigned int m_id;
unsigned int m_size;
int m_startAddr;
};
unsigned int** memory = calloc(MEMORY_SIZE, sizeof(unsigned int*));
Process** processList = calloc(MEMORY_SIZE, sizeof(Process*));
unsigned int itr;
unsigned int nb_process = 0;
int previous_process = -1;
for(itr = 0; itr < MEMORY_SIZE; ++itr)
{
if(memory[itr] != NULL)
{
previous_process = *memory[itr]; // works but risk of integer overflow
processList[nb_process]->m_id = *memory[itr]; // segfault
processList[nb_process]->m_size = 1;
processList[nb_process]->m_startAddr = itr;
nb_process++;
}
}
}
EDIT : I tried to make the following changes :
Process* processList = calloc(MEMORY_SIZE,sizeof(Process));
unsigned int** memory = calloc(MEMORY_SIZE, sizeof(unsigned int*));
unsigned int nb_process = 0;
int previous_process = -1;
unsigned int itr;
Process temp;
for(itr = 0; itr < MEMORY_SIZE; ++itr)
{
/* if memory unit is not occupied */
if(memory[itr] != NULL)
{
/* if process is not known yet */
if(*memory[itr] != previous_process)
{
previous_process = *memory[itr];
printf("previous_process %u \n", previous_process);
temp.m_id = *memory[itr];
temp.m_size = 1;
temp.m_startAddr = itr;
processList[nb_process] = temp;
nb_process++;
}
/* if the process is already known */
else
{
printf("size %u \n", processList[nb_process].m_size);
processList[nb_process].m_size++;
}
}
}
The output of previous_process is correct. However, the output of size got a problem. First, it has always two values below what it should (14 instead of 16, 30 instead of 32; etc...) at the end of the for loop. Worse, the count start at 0, while it should start at 1, since i initialize temp.m_size with one before copying it into processList. So the copy doesn't work... Why ? Should i use memcpy ?
You do this:
unsigned int** memory = calloc(MEMORY_SIZE, sizeof(unsigned int*));
*memory[0]
You allocated space for an array of pointers, but then you dereference those pointers. But they are all zero (which on most systems is NULL, but in any case is not a valid pointer because it came from nowhere). That can't work.
In addition to John's answer above you are also allocating an array of pointers
Process** processList = calloc(MEMORY_SIZE, sizeof(Process*));
unsigned int nb_process = 0;
You must ensure that each of that array of pointers is initialized with something valid before de-referencing them as they are.

Custom memcmp() of a struct with char member

I've written the following C code to compare two areas of memory and check if they are identical
#include <stdio.h>
struct s1 {
int a, b;
char c;
} s1;
int memcmpwannabe(void* value1, void *value2, int size1, int size2) {
if(size1 != size2)
return -1;
if(size1 == 0 || size2 == 0)
return -1;
//memcmp() wannabe
char *p1 = value1;
char *p2 = value2;
int sz = size1;
do {
if(*p1 != *p2)
break;
else
p1++, p2++;
}
while(--sz != 0);
if(sz == 0)
return 0;
return -1;
}
int main() {
struct s1 v1;
struct s1 v2;
v1.a = 1;
v1.b = 2;
v1.c = 'a';
v2.a = 1;
v2.b = 2;
v2.c = 'a';
int res = memcmpwannabe((void*) &v1, (void*) &v2, sizeof(s1), sizeof(s1));
printf("Res: %d\n", res);
}
The structures are identical, but it will return -1 anyway.
After some debugging i found that the 3 padding bytes after the char variable
are filled with random data, not with zeroes as i was expecting.
Knowing this and the fact that i want to keep it as generic as possible (so using void* as arguments), can somebody point me to an alternative byte to byte comparison?
(Before somebody asks, i'm writing a custom memcmp() because in some implementations it will continue after a difference
Knowing this and the fact that i want to keep it as generic as
possible (so using void* as arguments)
If you only take void * pointers, by definition you know nothing about the internal organization of the objects. Which means you know nothing about the meaning of the bytes (which are in "real" members and which are in hidden, padding-only members).
I would say this is not something that you can easily do with standard C.

void pointer and different size of memory

I have a function which converts a string of an unsigned int to its unsigned int counterpart.
I want to to be able to pass an unsigned type of ANY size to it as the container and specify a limit on how big of a value it is to hold. function prototype is this:
str_to_uint(void *tar, const char *str, const uint64_t lim)
uint64_t *tar is where the unsigned integer will be stored, *str is the string of the number and uint64_t lim is the limit of the size the *tar will be able to hold.
since sizeof(tar) is variable is it safe to cast *tar to an uint64_t * and then contain the converted variable in that? I know that it will always be smaller than the actual type anyway since I check for it with the lim variable.
is such a thing allowed?
basically it would boil down to this
I have a variable of an unsigned type where sizeof(variable) is 1, 2, 3 or 4.
I pass the variable to the function via (void *)&variable.
in the function I cast it to uint64_t * and write the detected variable into it. I make sure the detected variable is able to be written into the variable by checking if it is smaller or equal than lim
is this allowed?
code:
#include <stdio.h>
#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
static int str_to_uint(uint64_t *tar, const char *str, const uint64_t lim) {
char *eptr = NULL;
unsigned long long int temp = 0;
if (str == NULL) {
printf("str is a NULL pointer\n");
return -1;
}
temp = strtoull(str, &eptr, 10);
if (temp == 0 && eptr == str) {
printf("strtoull() conv err, %s\n", str);
return -1;
} else if (temp > lim) {
printf("strtoull() value to big to contain specified limit, %s\n", str);
return -1;
} else {
*tar = temp;
}
return 0;
}
int main() {
int ret;
uint8_t a;
uint16_t b;
uint32_t c;
uint64_t d;
ret = str_to_uint((void *)&a, "22", UINT8_MAX);
if (ret != 0) {
exit(1);
}
ret = str_to_uint((void *)&b, "22", UINT16_MAX);
if (ret != 0) {
exit(1);
}
ret = str_to_uint((void *)&c, "22", UINT32_MAX);
if (ret != 0) {
exit(1);
}
ret = str_to_uint((void *)&d, "22", UINT64_MAX);
if (ret != 0) {
exit(1);
}
printf("a = %"PRIu8"\nb = %"PRIu16"\nc = %"PRIu32"\nd = %"PRIu64"\n", a, b, c, d);
exit(0);
}
No, of course you can't do that.
If I call your function like this:
uint8_t my_little_value;
str_to_uint(&my_little_value, "4711", sizeof my_little_value);
Then you do
uint64_t *user_value = tar;
*user_value = ...;
Boom, you've overwritten a bunch of bytes you're not allowed to touch. Of course you knew this since I passed you the size of my variable, and you say you "make sure", but I don't see how you intend to do that if your approach is going to be treating tar as a uint64_t *.
I don't see why you can't just return the converted number, like strtoul() already does. That puts the responsibility for dealing with mismatch between storage location and potential precision to represent the converted number on the user (or even on the compiler!) where it belongs. Your proposed API is very error-prone and hard to understand.
At this line of code:
*tar = temp;
You are writing 8 bytes of data to a variable that may be less than 8 bytes in size. You need to handle each size separately, like this for 1 byte:
*(uint8_t *) tar = temp;

Heap block at X modified at Y past requested size - freeing allocated memory

Program crashing when freeing allocated memory: "Heap block at X modified at Y past requested size of 21".
Running function getUstr, crashes on free(uStr).
Can someone please help me find where am I using unallocated memory?
Thank you!
int HexToUChars(char* hexStr, unsigned char **str){
int i, n;
int strLen = strlen(hexStr)/2;
for (i = 0; i < (int)strLen; i++) {
sscanf_s(hexStr + 2 * i*sizeof(unsigned char), "%02X", *str+i*sizeof(unsigned char));
}
return 0;
}
int getUStr(){
char *hexStr = "E7CA7905DD060F0E437C885BF13DED9243B1D2BD94CB11223DA71360A8F7D2D4";
unsigned char *uStr;
size_t strLen = (size_t)strlen(hexStr) / 2;
uStr = calloc((strLen + 1), sizeof(unsigned char));
if (uStr != NULL){
HexToUChars(hexStr, &uStr);
free(uStr);//Error : Heap block at X modified at Y past requested size of 21
}
}
For sscanf() and friends, %02X expects a pointer to an unsigned int, but you're giving it a pointer to an unsigned char. If an unsigned int is, say, 4 bytes on your system, then you're going to write past the end of your allocated memory on the last couple of iterations through that loop.
You should provide a pointer to a (local) unsigned int to sscanf_s(), and then assign the value of that to your unsigned char.
there were several errors in the presented code.
those errors are fixed here
I did add a few $includes, etc so the file would compile
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int HexToUChars( const char *, unsigned char * );
int getUStr( void );
int HexToUChars(const char* pHexStr, unsigned char *pStr)
{
int i = 0; // loop index
//int n; // this raises a compiler warning about unused variable
// the strLen is using a reserved word, with only a capitalization change,
// I.E. very poor program practice
int hexStrLen = (int)strlen(pHexStr) >> 1; // this assumes that hexStr is an even number of bytes
for (i = 0; i < hexStrLen; i++)
{
pStr[i] = pHexStr[2*i] - '0';
pStr[i] += pHexStr[(2*i)+1] - '0';
} // end for
return 1;
} // end function: HexToUChars
int getUStr()
{
const char* pHexStr = "E7CA7905DD060F0E437C885BF13DED9243B1D2BD94CB11223DA71360A8F7D2D4";
unsigned char *pStr;
int hexStrLen = strlen(pHexStr) / 2;
if( NULL == (pStr = calloc( (hexStrLen + 1), sizeof(unsigned char) ) ) )
{
perror( "calloc failed" );
exit( EXIT_FAILURE );
}
// implied else, calloc successful
HexToUChars(pHexStr, pStr);
if( strlen((const char*)pStr) != hexStrLen )
{
perror( "problem in HexToUChars");
}
printf( "%s", pStr );
free(pStr);
return(0); // without this, compiler raises warning about no return statement
}

Why doesn't my C code snippet work? Simplistic version does. Passing args without VA_ARGS for unsigned long long

Basically I'm writing a printf function for an dedicated system so I want to pass an optional number of arguments without using VA_ARGS macros. I knocked up a simple example and this block of code works:
#include <stdio.h>
void func(int i, ...);
int main(int argc, char *argv);
int main(int argc, char *argv) {
unsigned long long f = 6799000015ULL;
unsigned long long *g;
//g points to f
g = &f;
printf("natural: %llu in hex: %llX address: %x\n", *g, *g, g);
//put pointer onto stack
func(6, g, g);
return 0;
}
void func(int i, ...) {
unsigned long long *f;
//pop value off
f = *(&i + 1);
printf("address: %x natural: %llu in hex: %llX\n", f, *f, *f);
}
However the larger example I'm trying to transfer this to doesn't work.
(in the main function):
unsigned long long f = 6799000015ULL;
unsigned long long *g;
g = &f;
kprintf("ull test: 1=%U 2=%X 3=%x 4= 5=\n", g, g, g);
(my dodgy printf function that I'm having trouble with. It maybe worth pointing out
this code DOES work with ints, char strings or anyother % flags which are passed by
value and not pointer. The only difference between what did work and the unsigned
long longs is one is bigger, so I pass by value instead to ensure I don't increment
the &format+ args part wrongly. Does that make sense?)
void kprintf(char *format, ...)
{
char buffer[KPRINTF_BUFFER_SIZE];
int bpos = 0; /* position to write to in buffer */
int fpos = 0; /* position of char to print in format string */
char ch; /* current character being processed*/
/*
* We have a variable number of paramters so we
* have to increment from the position of the format
* argument.
*/
int arg_offset = 1;
/*
* Think this through Phill. &format = address of format on stack.
* &(format + 1) = address of argument after format on stack.
* void *p = &(format + arg_offset);
* kprintf("xxx %i %s", 32, "hello");
* memory would look like = [ 3, 32, 5, "xxx", 32, "hello" ]
* get to 32 via p = &(format + 1); (int)p (because the int is copied, not a pointer)
* get to hello via p = &(format + 2); (char*)p;
*/
void *arg;
unsigned long long *llu;
arg = (void*) (&format + arg_offset);
llu = (unsigned long long*) *(&format + arg_offset);
while (1)
{
ch = format[fpos++];
if (ch == '\0')
break;
if (ch != '%')
buffer[bpos++] = ch;
else
{
ch = format[fpos++];
if (ch == 's')
bpos += strcpy(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, (char*)arg);
else if (ch == '%')
buffer[bpos++] = '%';
else if (ch == 'i')
bpos += int_to_str(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *((int*)arg));
else if (ch == 'x')
bpos += int_to_hex_str(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *((int*)arg));
else if (ch == 'o')
bpos += int_to_oct_str(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *((int*)arg));
else if (ch == 'X') {
//arg is expected to be a pointer we need to further dereference.
bpos += unsigned_long_long_to_hex(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *llu);
} else if (ch == 'U') {
bpos += unsigned_long_long_to_str(&buffer[bpos], KPRINTF_BUFFER_SIZE - bpos, *llu);
} else
{
puts("invalid char ");
putch(ch);
puts(" passed to kprintf\n");
}
arg_offset++;
arg = (void *)(&format + arg_offset);
llu = (unsigned long long*) *(&format + arg_offset);
}
}
buffer[bpos] = '\0';
puts(buffer);
}
(and the unsigned long long functions it goes on to call):
int unsigned_long_long_to_hex(char *buffer, int max_size, unsigned long long number)
{
return ull_number_to_str(buffer, max_size, number, BASE_HEX);
}
int unsigned_long_long_to_str(char *buffer, int max_size, unsigned long long number) {
return ull_number_to_str(buffer, max_size, number, BASE_DECIMAL);
}
int ull_number_to_str(char *buffer, int max_size, unsigned long long number, int base) {
int bufpos = 0;
unsigned int lo_byte = (unsigned int) number;
unsigned int hi_byte = (unsigned int) (number >> 32);
bufpos = number_to_str(buffer, max_size, lo_byte, base);
bufpos += number_to_str(buffer + bufpos, max_size, hi_byte, base);
return bufpos;
}
#define NUMERIC_BUFF_SIZE (11 * (ADDRESS_SIZE / 32))
int number_to_str(char *buffer, int max_size, int number, int base)
{
char *char_map = "0123456789ABCDEF";
int remain = 0;
char buff_stack[NUMERIC_BUFF_SIZE];
int stk_pnt = 0;
int bpos = 0;
/* with this method of parsing, the digits come out backwards */
do
{
if (stk_pnt > NUMERIC_BUFF_SIZE)
{
puts("Number has too many digits to be printed. Increasse NUMBERIC_BUFF_SIZE\n");
return 0;
}
remain = number % base;
number = number / base;
buff_stack[stk_pnt++] = char_map[remain];
} while (number > 0);
/* before writing...ensure we have enough room */
if (stk_pnt > max_size)
{
//error. do something?
puts("number_to_str passed number with too many digits to go into buffer\n");
//printf("error. stk_pnt > max_size (%d > %d)\n", stk_pnt, max_size);
return 0;
}
/* reorder */
while (stk_pnt > 0)
buffer[bpos++] = buff_stack[--stk_pnt];
return bpos;
}
Sorry guys, I can't see what I've done wrong. I appreciate this is a "wall of code" type scenario but hopefully someone can see what I've done wrong. I appreciate you probably dislike not using VA_ARGS but I don't understand why this technique shouldn't just work? And also, I'm linking with -nostdlib too. If someone can help I'd really appreciate it. Also, this isn't meant to be production quality code so if I lack some C fundamentals feel free to be constructive about it :-)
It's a bad idea to code this way. Use stdarg.h.
On the off chance (I presume this based on the name kprintf) that you're working on a hobby kernel or embedded project and looking to avoid using standard libraries, I recommend at least writing your own (architecture and compiler specific) set of stdarg macros that conform to the well-known interfaces and code against that. That way your code doesn't look like such a WTF by dereferencing past the address of the last argument.
You can make a va_list type that stores the last-known address, and your va_arg macro could appropriately align the sizeof of the type it's passed and advance the pointer accordingly. For most conventions I have worked on for x86, every type is promoted to 32 bits...
You have to read on the calling conventions for your platform, i.e. how on your target processor/OS function arguments are passed, and how registers are saved. Not all parameters are passed on stack. Depending on number of parameters and their types, many complex situations can arise.
I should add: if you want to manipulate the stack by hand as you are doing above, you need to do it in assembler, not in C. The C language follows a defined standard, and what you are doing above it not legal code (i.e., its meaning is not well-defined). As such, the compiler is allowed to do anything it wants with it, such as optimize it in weird ways unsuitable to your needs.

Resources