Jump to content

Can't figure why I am getting "unhandled exception"

Go to solution Solved by Ciccioo,

scanf_s should look more like

scanf_s("%39s", name, 40);

It's required, since it is what makes scanf_s safer than scanf

I can't seem to figure out why I keep getting: 

 

"Unhandled exception at 0x77DE3FD4 (msvcr120d.dll) in Exercise.exe: 0xC0000005:

Access violation writing location 0x00DA1000"

 

when I try to compile this code written in C:

//name program#include <stdio.h>int main(){	char name[40];	printf("What's your name? ");	scanf_s("%s", name);	printf("Hi, %s.", name);	getchar();	getchar();	return 0;}

Any ideas?

 

Thanks!

ASRock B550M PG RIPTIDE       Corsair Vengeance 16 GB DDR4             TEAMGROUP MP33 1 TB NVME SSD

AMD Ryzen 5 5600X                   Antec DF700 Case                                 MSI Radeon RX 580 4 GB ARMOR OC

 

Link to comment
Share on other sites

Link to post
Share on other sites

Ah an access violation error.
Those usually happens when you are writing to memory that is not allocated to your program.

Can you try and find out on what exact line it is crashing?
I don't know what IDE you are using, but you should be able to set a break point somewhere and then step though your code. If you don't have breakpoints, try printing a different message on every line. One trick I use is printf("line: %d was hit\n", __LINE__);

Link to comment
Share on other sites

Link to post
Share on other sites

scanf_s should look more like

scanf_s("%39s", name, 40);

It's required, since it is what makes scanf_s safer than scanf

Link to comment
Share on other sites

Link to post
Share on other sites

scanf_s should look more like

scanf_s("%39s", name, 40);

It's required, since it is what makes scanf_s safer than scanf

 

Oh, man. You don't know how crazy this was driving me. Thank you. My text doesn't cover scanf_s, which seems to be required by Visual Studio. I think it does make a brief mention that some compilers don't use scanf, but I couldn't remember where it said that. 

 

Ah an access violation error.

Those usually happens when you are writing to memory that is not allocated to your program.

Can you try and find out on what exact line it is crashing?

I don't know what IDE you are using, but you should be able to set a break point somewhere and then step though your code. If you don't have breakpoints, try printing a different message on every line. One trick I use is printf("line: %d was hit\n", __LINE__);

 

Thanks, man. @Ciccioo figured it out. I do need to practice debugging with breakpoints though. I am using Visual Studio on this machine but Netbeans on another, whose debugging I've got down a little better. 

ASRock B550M PG RIPTIDE       Corsair Vengeance 16 GB DDR4             TEAMGROUP MP33 1 TB NVME SSD

AMD Ryzen 5 5600X                   Antec DF700 Case                                 MSI Radeon RX 580 4 GB ARMOR OC

 

Link to comment
Share on other sites

Link to post
Share on other sites

scanf_s should look more like

scanf_s("%39s", name, 40);

It's required, since it is what makes scanf_s safer than scanf

 

Also, I'm assuming this is allocating 39 memory cells to the characters (minus the null value) within a 40 cell array, right? This is the format for all scanf_s arguments?

ASRock B550M PG RIPTIDE       Corsair Vengeance 16 GB DDR4             TEAMGROUP MP33 1 TB NVME SSD

AMD Ryzen 5 5600X                   Antec DF700 Case                                 MSI Radeon RX 580 4 GB ARMOR OC

 

Link to comment
Share on other sites

Link to post
Share on other sites

Also, I'm assuming this is allocating 39 memory cells to the characters (minus the null value) within a 40 cell array, right? This is the format for all scanf_s arguments?

scanf_s doesn't allocate memory to store the data it reads, it relies on the parameters that you pass to it. the problem is that it needs to know how big those buffers are.

 

in the format string "%39s" you tell scanf_s that your buffer can hold a string of up to 39 characters.

the third parameter, 40, says that the buffer is 40 characters long.

if in the format you say "%40s" it will read a not-null-terminated string of up to 40 characters

 

https://msdn.microsoft.com/en-US/en-en/library/9y6s16x1.aspx

Link to comment
Share on other sites

Link to post
Share on other sites

Ah, I totally missed the fact that it's the safe version. I personally hate the safe versions of all those functions, since they are windows only.

Link to comment
Share on other sites

Link to post
Share on other sites

Just so you're aware of why this fails, the MSVC++ compiler implements buffer overflow checks when you use the /GS flag. The runtime generates a random "cookie" value and places it on the end of the buffer. This is usually done for the stack (where they're also known as stack canaries), but cookies can be (and are) implemented on the heap too. In this case it's a stack cookie, though. When a runtime call "taints" a checked buffer by writing to it, the compiler injects a call to verify that the expected cookie value remains in memory, before the runtime call returns.
 
The reason this is used is that, in the case of the stack, you can cause the buffer overflow to overwrite the return address of the scanf call, allowing for arbitrary code execution. In the heap this is a bit more complicated, but typically the trick is to overwrite another object's vtable so that method calls point to an attacker-controlled address.
 
If you look at the ___security_cookie_init function in a compiled application, you can reverse engineer how it creates the cookies. Here's an example:

FILETIME systemTime;LARGE_INTEGER perfCount;systemTime.dwLowDateTime = 0;systemTime.dwHighDateTime = 0;int perfValue = 0;int newSecurityCookie = 0; // is the current security cookie set to the default security cookie?if (___security_cookie == DEFAULT_COOKIE || ___security_cookie == 0xFFFF0000){    // make new cookie!    GetSystemTimeAsFileTime(&systemTime);    newSecurityCookie = systemTime.dwLowDateTime ^ systemTime.dwHighDateTime;    newSecurityCookie ^= GetCurrentThreadId();    newSecurityCookie ^= GetCurrentProcessId();    QueryPerformanceCounter(&perfCount);    perfValue = (perfCount & 0xFFFFFFFF) ^ (perfCount >> 32);    newSecurityCookie ^= perfValue;        // ensure a valid cookie was set    ___security_cookie = newSecurityCookie ? cookie : DEFAULT_COOKIE;    ___security_cookie_complement = !__security_cookie;}

The DEFAULT_COOKIE value is a random value baked in at compile time.

Workstation: 2x Xeon 8276L (56 cores), 192GB ECC RAM, 4x NVMe SSD in VROC, 2080Ti, Chelsio T520 2x10G NIC

NAS: TrueNAS, 8x 6TB WD Red Pro in RAIDZ2, 96GB RAM, 1TB NVMe L2ARC, Chelsio T520 2x10G NIC

Link to comment
Share on other sites

Link to post
Share on other sites

Ah, I totally missed the fact that it's the safe version. I personally hate the safe versions of all those functions, since they are windows only.

 

I guess that's why a lot of people prefer linux, huh. I never heard of the safe versions until getting help here. 

ASRock B550M PG RIPTIDE       Corsair Vengeance 16 GB DDR4             TEAMGROUP MP33 1 TB NVME SSD

AMD Ryzen 5 5600X                   Antec DF700 Case                                 MSI Radeon RX 580 4 GB ARMOR OC

 

Link to comment
Share on other sites

Link to post
Share on other sites

Just so you're aware of why this fails, the MSVC++ compiler implements buffer overflow checks when you use the /GS flag. The runtime generates a random "cookie" value and places it on the end of the buffer. This is usually done for the stack (where they're also known as stack canaries), but cookies can be (and are) implemented on the heap too. In this case it's a stack cookie, though. When a runtime call "taints" a checked buffer by writing to it, the compiler injects a call to verify that the expected cookie value remains in memory, before the runtime call returns.

 

The reason this is used is that, in the case of the stack, you can cause the buffer overflow to overwrite the return address of the scanf call, allowing for arbitrary code execution. In the heap this is a bit more complicated, but typically the trick is to overwrite another object's vtable so that method calls point to an attacker-controlled address.

 

If you look at the ___security_cookie_init function in a compiled application, you can reverse engineer how it creates the cookies. Here's an example:

FILETIME systemTime;LARGE_INTEGER perfCount;systemTime.dwLowDateTime = 0;systemTime.dwHighDateTime = 0;int perfValue = 0;int newSecurityCookie = 0; // is the current security cookie set to the default security cookie?if (___security_cookie == DEFAULT_COOKIE || ___security_cookie == 0xFFFF0000){    // make new cookie!    GetSystemTimeAsFileTime(&systemTime);    newSecurityCookie = systemTime.dwLowDateTime ^ systemTime.dwHighDateTime;    newSecurityCookie ^= GetCurrentThreadId();    newSecurityCookie ^= GetCurrentProcessId();    QueryPerformanceCounter(&perfCount);    perfValue = (perfCount & 0xFFFFFFFF) ^ (perfCount >> 32);    newSecurityCookie ^= perfValue;        // ensure a valid cookie was set    ___security_cookie = newSecurityCookie ? cookie : DEFAULT_COOKIE;    ___security_cookie_complement = !__security_cookie;}

The DEFAULT_COOKIE value is a random value baked in at compile time.

 

I admit that went a little over my head, but I think I sort of understand. You are giving the buffer variables to scanf_s to override generated cookie values from what it sounds like.....

ASRock B550M PG RIPTIDE       Corsair Vengeance 16 GB DDR4             TEAMGROUP MP33 1 TB NVME SSD

AMD Ryzen 5 5600X                   Antec DF700 Case                                 MSI Radeon RX 580 4 GB ARMOR OC

 

Link to comment
Share on other sites

Link to post
Share on other sites

I admit that went a little over my head, but I think I sort of understand. You are giving the buffer variables to scanf_s to override generated cookie values from what it sounds like.....

 

Not quite. The function I showed is called when the program starts, and is used to generate a "random" (i.e. difficult to predict) stack cookie value for that instance of the program.

 

This cookie value sits in the memory of the program, and is then used in the "prolog" and "epilog" of certain runtime functions like scanf and sprintf. The prolog and epilog are small chunks of assembly code generated by the compiler that run at the start and end of a function respectively. So, the prolog of scanf would write the stack cookie to the end of the buffer, in some slack space left there by the compiler for this use. The epilog then checks that the same stack cookie value is still there at the end of the scanf function, to ensure that the buffer wasn't overrun. If the cookie isn't right, it forcibly crashes the program.

 

The reason for using a random cookie is that, if an attacker knew what the cookie was ahead of time, they could overwrite the buffer and keep the cookie intact, allowing them to corrupt program flow and execute arbitrary code. This is called a stack buffer overflow exploit.

Workstation: 2x Xeon 8276L (56 cores), 192GB ECC RAM, 4x NVMe SSD in VROC, 2080Ti, Chelsio T520 2x10G NIC

NAS: TrueNAS, 8x 6TB WD Red Pro in RAIDZ2, 96GB RAM, 1TB NVMe L2ARC, Chelsio T520 2x10G NIC

Link to comment
Share on other sites

Link to post
Share on other sites

Not quite. The function I showed is called when the program starts, and is used to generate a "random" (i.e. difficult to predict) stack cookie value for that instance of the program.

 

This cookie value sits in the memory of the program, and is then used in the "prolog" and "epilog" of certain runtime functions like scanf and sprintf. The prolog and epilog are small chunks of assembly code generated by the compiler that run at the start and end of a function respectively. So, the prolog of scanf would write the stack cookie to the end of the buffer, in some slack space left there by the compiler for this use. The epilog then checks that the same stack cookie value is still there at the end of the scanf function, to ensure that the buffer wasn't overrun. If the cookie isn't right, it forcibly crashes the program.

 

The reason for using a random cookie is that, if an attacker knew what the cookie was ahead of time, they could overwrite the buffer and keep the cookie intact, allowing them to corrupt program flow and execute arbitrary code. This is called a stack buffer overflow exploit.

 

Oh, ok. So the fact that I didn't include prolog and epilog values in my original code tells the system, that "Hey, you didn't take proper security measures. Someone could use a stack buffer overflow exploit."?

ASRock B550M PG RIPTIDE       Corsair Vengeance 16 GB DDR4             TEAMGROUP MP33 1 TB NVME SSD

AMD Ryzen 5 5600X                   Antec DF700 Case                                 MSI Radeon RX 580 4 GB ARMOR OC

 

Link to comment
Share on other sites

Link to post
Share on other sites

Oh, ok. So the fact that I didn't include prolog and epilog values in my original code tells the system, that "Hey, you didn't take proper security measures. Someone could use a stack buffer overflow exploit."?

 

No, the fact that you set the /GS flag on the compiler (which is the default for Visual Studio) caused the necessary prolog and epilog code to be automatically injected into every call that pushes a buffer to the stack. It's compiler magic.

Workstation: 2x Xeon 8276L (56 cores), 192GB ECC RAM, 4x NVMe SSD in VROC, 2080Ti, Chelsio T520 2x10G NIC

NAS: TrueNAS, 8x 6TB WD Red Pro in RAIDZ2, 96GB RAM, 1TB NVMe L2ARC, Chelsio T520 2x10G NIC

Link to comment
Share on other sites

Link to post
Share on other sites

No, the fact that you set the /GS flag on the compiler (which is the default for Visual Studio) caused the necessary prolog and epilog code to be automatically injected into every call that pushes a buffer to the stack. It's compiler magic.

 

Hmm. I'm still quite the beginner but I'll have to add this to my memory banks

ASRock B550M PG RIPTIDE       Corsair Vengeance 16 GB DDR4             TEAMGROUP MP33 1 TB NVME SSD

AMD Ryzen 5 5600X                   Antec DF700 Case                                 MSI Radeon RX 580 4 GB ARMOR OC

 

Link to comment
Share on other sites

Link to post
Share on other sites

No, the fact that you set the /GS flag on the compiler (which is the default for Visual Studio) caused the necessary prolog and epilog code to be automatically injected into every call that pushes a buffer to the stack. It's compiler magic.

 

The fact that it's a default setting like that for Visual Studio, which I was using, definitely threw me for a loop

ASRock B550M PG RIPTIDE       Corsair Vengeance 16 GB DDR4             TEAMGROUP MP33 1 TB NVME SSD

AMD Ryzen 5 5600X                   Antec DF700 Case                                 MSI Radeon RX 580 4 GB ARMOR OC

 

Link to comment
Share on other sites

Link to post
Share on other sites

The fact that it's a default setting like that for Visual Studio, which I was using, definitely threw me for a loop

 

The reason it's set is to provide secure code. In fact, if it had not set /GS, you would've had a program that *maybe* appeared to work, but sometimes crashed or dumped out garbage data. It'd have been much more difficult to diagnose the fault.

 

Some developers switch off security features like stack cookies, SafeSEH, ASLR, DEP opt-in, etc. in their compiler to "make things simpler", and then their code makes it into production, and a bad person makes a whole mess of someone's corporate network and steals all their secrets. Then I get called in to analyse the software and find the security vulnerabilities, and they have to pay my employer lots of money for my time, AND fix the problems afterwards anyway.

 

Best to leave them on, as they are by default! :)

Workstation: 2x Xeon 8276L (56 cores), 192GB ECC RAM, 4x NVMe SSD in VROC, 2080Ti, Chelsio T520 2x10G NIC

NAS: TrueNAS, 8x 6TB WD Red Pro in RAIDZ2, 96GB RAM, 1TB NVMe L2ARC, Chelsio T520 2x10G NIC

Link to comment
Share on other sites

Link to post
Share on other sites

The reason it's set is to provide secure code. In fact, if it had not set /GS, you would've had a program that *maybe* appeared to work, but sometimes crashed or dumped out garbage data. It'd have been much more difficult to diagnose the fault.

 

Some developers switch off security features like stack cookies, SafeSEH, ASLR, DEP opt-in, etc. in their compiler to "make things simpler", and then their code makes it into production, and a bad person makes a whole mess of someone's corporate network and steals all their secrets. Then I get called in to analyse the software and find the security vulnerabilities, and they have to pay my employer lots of money for my time, AND fix the problems afterwards anyway.

 

Best to leave them on, as they are by default! :)

 

Netbeans must be an example. They seem like the sort of simpler, maybe not as sophisticated compiler. Netbeans lets me use scanf

 

And cool! That sounds like a good job. I'm trying to get my feet landing in some sort of beginner/associate programming position after I graduate college

ASRock B550M PG RIPTIDE       Corsair Vengeance 16 GB DDR4             TEAMGROUP MP33 1 TB NVME SSD

AMD Ryzen 5 5600X                   Antec DF700 Case                                 MSI Radeon RX 580 4 GB ARMOR OC

 

Link to comment
Share on other sites

Link to post
Share on other sites

Netbeans must be an example. They seem like the sort of simpler, maybe not as sophisticated compiler. Netbeans lets me use scanf

 

 

Netbeans doesn't have the same code analysis built into it. Visual Studio uses the Microsoft Secure Development Lifecycle (SDL) standard.

Workstation: 2x Xeon 8276L (56 cores), 192GB ECC RAM, 4x NVMe SSD in VROC, 2080Ti, Chelsio T520 2x10G NIC

NAS: TrueNAS, 8x 6TB WD Red Pro in RAIDZ2, 96GB RAM, 1TB NVMe L2ARC, Chelsio T520 2x10G NIC

Link to comment
Share on other sites

Link to post
Share on other sites

Netbeans doesn't have the same code analysis built into it. Visual Studio uses the Microsoft Secure Development Lifecycle (SDL) standard.

 

Learning the ins and outs is tricky. It's sort of one of the things I feel is a hurdle to new programmers like me. Seems like there is so much to learn regarding different compilers and such. Not to mention the amount of languages in use out there

ASRock B550M PG RIPTIDE       Corsair Vengeance 16 GB DDR4             TEAMGROUP MP33 1 TB NVME SSD

AMD Ryzen 5 5600X                   Antec DF700 Case                                 MSI Radeon RX 580 4 GB ARMOR OC

 

Link to comment
Share on other sites

Link to post
Share on other sites

this code written in C

I just thought I should note, MSVC isn't actually C99 compliant, so you may run into weird problems when you compile with MSVC. I don't think it allows you to declare pointers restrict, for example. Also, the Visual Studio debugger is very limited in that if you declare an int*, it will not allow you to view it as an array for some reason and if you declare a void*, you can't just recast it to whatever pointer you want to see the variable value as. For these reasons I'd recommend looking into GCC and GDB, since they do not have these rather annoying problems.

 

Also, I've noticed that the MSVC parser is easy to just flat out break on perfectly valid code. Sometimes it'll just tell me "expecting ';'" even though all the code I'd written was legal. This happens a lot less on VS2013 than VS2010 though.

Link to comment
Share on other sites

Link to post
Share on other sites

I just thought I should note, MSVC isn't actually C99 compliant

But rather it's compliant with C11 and all the newer ones?  I use GCC for my Netbeans and I've used it on Linux. I don't remember what I needed to do for Visual Studio. I guess I didn't have to do anything. 

ASRock B550M PG RIPTIDE       Corsair Vengeance 16 GB DDR4             TEAMGROUP MP33 1 TB NVME SSD

AMD Ryzen 5 5600X                   Antec DF700 Case                                 MSI Radeon RX 580 4 GB ARMOR OC

 

Link to comment
Share on other sites

Link to post
Share on other sites

But rather it's compliant with C11 and all the newer ones?  I use GCC for my Netbeans and I've used it on Linux. I don't remember what I needed to do for Visual Studio. I guess I didn't have to do anything. 

I don't think C11 removed the restrict keyword...

 

if anyone was wondering I'm not just bad at C. http://cellperformance.beyond3d.com/articles/2006/05/demystifying-the-restrict-keyword.html

Not all compilers are compliant with the C99 standard. For example Microsoft's compiler, does not support the C99 standard at all. If you are using MSVC on a x86 platform you will not have access to this critical optimization option.

Link to comment
Share on other sites

Link to post
Share on other sites

I don't think C11 removed the restrict keyword...

 

if anyone was wondering I'm not just bad at C. http://cellperformance.beyond3d.com/articles/2006/05/demystifying-the-restrict-keyword.html

 

I'm still absorbing a lot of the beginner programming stuff myself. This stuff still sort of goes over my head

ASRock B550M PG RIPTIDE       Corsair Vengeance 16 GB DDR4             TEAMGROUP MP33 1 TB NVME SSD

AMD Ryzen 5 5600X                   Antec DF700 Case                                 MSI Radeon RX 580 4 GB ARMOR OC

 

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×