I have this simple code which causes a segfault in initstate_r:
#include <stdlib.h>
#include <stdio.h>
int main (int argc, char *argv[])
{
int *test[8];
struct random_data rstate;
char random_bin[256];
initstate_r(1,random_bin,256,&rstate);
test[0] = NULL;
printf("%p",test[0]);
return 0;
}
It does not produce a segfault if int *test[8] lines are removed.
It doesn't seem to cause a segfault on most linux systems, but it does on ubuntu linux subsystem for windows gcc (or maybe that is just luck)?
Is my use of initstate_r actually wrong and I just get lucky sometimes? I don't see anything wrong with it?
Thanks!
From the initstate_r manual page:
Before calling this function, the buf.state field must be initialized to NULL.
You pass a pointer to the uninitialized structure rstate. That means all members of the structure will be uninitialized and have indeterminate values. If the initstate_r attempt to access these members then it could lead to undefined behavior.
You need to initialize at least the state member of the structure to a null pointer:
rstate.state = NULL;
Related
Running this code
#include <stdlib.h>
#include <stdio.h>
char* f() {
char s[] = "Hello, world!";
return s;
}
int main() {
printf("%s", f());
return 0;
}
prints (null). This makes sense to me: The char array is valid only in the scope of f, so the pointer returned is not valid. However, if I create a struct containing the pointer to the array
#include <stdlib.h>
#include <stdio.h>
typedef struct word {
char* str;
} word;
word f() {
char str[] = "Hello, world!";
return (word) { str };
}
int main() {
printf("%s", f().str);
return 0;
}
this will now "work", i. e. it will print Hello, world!.
Now I am wondering why it does; shouldn't the pointer here be invalid too, because the array it points to was created in f and is thus not valid anymore? Or is this just undefined behavior and I got "lucky" that nothing else was pushed onto the stack at that location? Or is there a mistake in my reasoning (I am rather new to C).
I haven't been able to find information on this specific behavior, only for structs containing arrays of constant size. I have tested this using gcc version 9.4.0 (which uses gnu11 "standard" as default). I would appreciate if anyone could point me to the explanation of this behavior.
Or is this just undefined behavior and I got "lucky" that nothing else was pushed onto the stack at that location?
That is correct. The line
printf("%s", f().str);
will invoke undefined behavior by dereferencing a dangling pointer. This means that anything may happen; your program may crash or it may appear to work as intended.
The compiler clang is actually able to detect this error at run-time, if you compile with -fsanitize=address. Here is an online demonstration.
For further explanation, you may want to read this answer to a similar C++ question, which contains an interesting analogy. Most of the information in the answer also applies to C.
Recently, I wrote the following, buggy, c code:
#include <stdio.h>
struct IpAddr {
unsigned char a, b, c, d;
};
struct IpAddr ipv4_from_str (const char * str) {
struct IpAddr res = {0};
sscanf(str, "%d.%d.%d.%d", &res.a, &res.b, &res.c, &res.d);
return res;
}
int main (int argc, char ** argv) {
struct IpAddr ip = ipv4_from_str("192.168.1.1");
printf("Read in ip: %d.%d.%d.%d\n", ip.a, ip.b, ip.c, ip.d);
return 0;
}
The bug is that I use %d in sscanf, while supplying pointers to the 1-byte-wide unsigned char. %d accepts a 4-byte wide int pointer, and the difference results in an out-of-bounds write. out-of-bound write is definitely ub, and the program will crash.
My confusion is in the non-constant nature of the bug. Over 1,000 runs, the program segfaults before the print statement 50% of the time, and segfaults after the statement the other 50% of the time. I don't understand why this can change. What is the difference between two invocations of the program? I was under the impression that the memory layout of the stack is consistent, and small test programs I've written seem to confirm this. Is that not the case?
I'm using gcc v11.3.0 on Debian bookworm, kernel 5.14.16-1, and I compiled without any flags set.
Here is the assembly output from my compiler for reference.
Undefined behavior means that anything can happen, even inconsistent results.
In practice, this inconsistency is most likely due to Address Space Layout Randomization. Depending on how the data is located in memory, the out-of-bounds accesses may or may not access unallocated memory or overwrite a critical pointer.
See also Why don't I get a segmentation fault when I write beyond the end of an array?
I decided to check what value I'll get when I print the memory content of an instance of a struct:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
int value;
} Data;
int main(){
Data *d = (Data*) malloc(sizeof(Data));
printf("%d", *d);
return 0;
}
The result I got is a random value (To me) and I tried to do all kinds of experiments but not so I could understand what is behind this value. But maybe I just do not understand the way structs are stored in memory. I'd love an explanation.
The memory returned from malloc is uninitialized. Reading it before it is set will result in seeing indeterminate values (i.e. the random values you're seeing), and in some cases can trigger undefined behavior. There's no guarantee you'll even read the same value twice from the same memory location.
The way you're printing is also incorrect. You're passing an instance of a struct to printf when the given format specifier expects an int. That also causes undefined behavior.
I am very new to both using Linux and creating anything remotely serious on C. I've been trying to create a program which will simply compress a single string, but I keep getting this Segmentation fault when trying to run the compiled file.
I compiled it using:
gcc 2.c -o test.o -lz
My code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#include <assert.h>
int main ()
{
char *istream = malloc(10), *ostream = malloc(120);
istream = "istream";
int res = compress(ostream, (uLongf *)strlen(ostream), istream,(ulong)strlen(istream));
return 0;
}
Could anyone explain to me why this error is happening and how can I improve my code?
This line would appear to be the main problem:
(uLongf*)strlen(ostream)
You are interpreting a size_t value as a pointer to a buffer. You are meant to pass the address of an unsigned long containing the length of the output buffer. Take another look at the documentation for compress.
On top of that you don't yet understand how C strings work. The assignment operator when used with a char* lvalue merely copies an address and not the contents of a string. I suggest that you declare your buffers like this:
const char *istream = "istream";
char ostream[120];
I think your program should be something along these lines:
int main(void)
{
const char *istream = "istream";
char ostream[120];
uLongf destLen = sizeof(ostream);
int res = compress((Bytef*)ostream, &destLen, (Bytef*)istream, strlen(istream));
return 0;
}
Note that I wrote the code assuming that you are using a C compiler. And hence int main(void).
First you make istream point to memory you allocate:
char *istream = malloc(10)
then you make it point to a literal (and therefore constant and read-only) string:
istream = "istream";
You need to copy the string literal into the allocated memory, or you will no longer have the original pointer you allocated and have a memory leak. You also will not be able to free that pointer, since istream points to something you haven't allocated with malloc.
As for the crash, see the answer by David Heffernan.
As a side-note, there is no C++ in your code, only pure and plain C.
Change:
istream = "istream"
To
strcpy(istream,"istream");
In addition, what did you expect strlen(ostream) to return? 120?
strlen returns the index of the first 0 character encountered within the input string.
In your case, the contents of the memory pointed by ostream is unknown (i.e. "junk").
strlen will scan this memory until a 0 character is encountered, but will probably exceed the 120-byte memory space and cause a memory access violation.
Change strlen(ostream) to 120 if that was your intention.
Hi I am trying to make a program with pointer in a struct. Compiler appears no problems but the program crashes.
Could you help me please ?
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int pos;
typedef struct _Parking Parking;
struct _Parking
{
int pos;
char name[15];
char description[80];
float price;
int slots[5];
char last_driver_id;
int reservations;
};
Parking *myaccounts;
int create_Parking()
{
strcpy(myaccounts->name,"Pro");
myaccounts->pos ++;
return pos-1;
}
int main()
{
int a;
a = create_Parking();
printf("a=%d\n",a);
printf("name=%s\n",myaccounts->name);
system("pause");
return 0;
}
Your myaccounts pointer is initialized to NULL (as a global variable) and does therefore not point to usable memory. Try the manual page for malloc for more information.
Edit: Incorporated Maciej's comment.
You never allocate any memory for "myaccounts".
Pointers in C do not point to valid memory (and will crash if you try to use them), until you specifically point them somewhere valid by using the address-of operator on an object (&) or by allocating memory for them and assigning that address into the pointer (malloc() and friends). Of course if you use the address-of operator that location can go invalid when the object goes out of scope. If you use malloc() that location can go invalid when free() is called. In either case, your pointer will become invalid again.
C is hugely reliant on pointers too, so you can count on any C code you write of any size having a bug or two of this nature until you track them down and fix them. Getting your sources past the compiler in C really doesn't mean much. If you want to write in a language where your code is liable to work first time you run it after getting it past the compiler, you want Ada.
Your pointer doesn't point to anything. You can try either one of these:
Parking myaccountsInstance;
Parking *myaccounts = &myaccountsInstance;
Or in the main function:
Start with:
myaccounts = (Parking*)malloc(sizeof(Parking));
And end with:
free(myaccounts);