Why can I store more chars than I reserve a place for?

nsaspook

Joined Aug 27, 2009
16,438
Oh, and the most concise and, from a certain perspective, most accurate and useful answer to the question in the thread title:

"Why can I store more chars than I reserve a place for?"

is: "Because C gives you plenty of rope with which to hang yourself."
C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do it blows your whole leg off.

Bjarne Stroustrup
 

Thread Starter

StrongPenguin

Joined Jun 9, 2018
307
Interesting debate going on here.

I'm trying to make my own way to get around this problem, just to get some practice playing with arrays and memory. Since I'm at the IF-ELSE part of the book, I figure try to incorporate that.

Code:
#include <stdio.h>

#define NAMELENGTH 7

char name[8]; // 7 chars, room for Zeroterm
char tempNameStore[]; //store name in a bufffer, then move it into name var if correct size

main()
{
    printf("Say my name, say my name..\n");
    scanf("%s,\n", &tempNameStore);

    if (sizeof(tempNameStore) <= sizeof(name))
    {
        printf("You entered a correct name length, and thus did not cause an overflow.\n");
        printf("%s", tempNameStore);
    }
    else
    {
        printf("No good.");
    }
    return 0;
}
But it doesn't work. I can compile an undefined array tempNameStore, but I can't use the sizeof operator after I have stored the value from scanf.

Is this impossible?
 

WBahn

Joined Mar 31, 2012
33,076
I'm surprised it didn't throw an error trying to compile a global array with no means of evaluating it's size. What compiler are you using?

The basic approach you are using won't work. The value returned by sizeof() is evaluated at compile time (with some caveats for the variable-length array declarations allowed in later versions of the standard) and it evaluates to the amount of memory allocated to the data unit, not the amount of memory actually used at run time. What you would need to do in your approach is to allocate an excessively large array for tempNameStore[] -- long enough that you are willing to assume that no one will ever attempt to enter a string longer than that, which is a fundamentally bad assumption in an adversarial situation -- and then use strlen() to find out the length of the string that was actually entered.

The better way is to bring the information into a dynamically allocated array using fgets() and if the newline character isn't at the end of the string, then reallocate the memory and call the function again bringing in the next chunk at the existing input's NUL terminator. Continue doing this until the input contains the newline character and then (in most cases) replace the newline character with a NUL. Now you can process the string as appropriate knowing that you safely got the entire string and the input buffer is ready for the next input operation.
 

Thread Starter

StrongPenguin

Joined Jun 9, 2018
307
I'm using Code Blocks17.12. There were no problems compiling, just the sizeof(tempNameStore)

Using strlen() worked! This is a really poor way of solving this issue, but I sure did learn a thing or two.

I am going to continue reading the book, then I will try some work with fgets() and the method you mentioned. I don't wanna jump too much ahead.
 

WBahn

Joined Mar 31, 2012
33,076
I'm using Code Blocks17.12. There were no problems compiling, just the sizeof(tempNameStore)

Using strlen() worked! This is a really poor way of solving this issue, but I sure did learn a thing or two.

I am going to continue reading the book, then I will try some work with fgets() and the method you mentioned. I don't wanna jump too much ahead.
I've never used CodeBlocks personally, but I haven't gotten a very favorable impression of it from discussions with those that have.

The fact that it will compile a storage element for which sizeof() can't evaluate the size is just one more troubling point not in its favor.

It might be interesting to scope out the memory map involved, but my guess is that you are invoking undefined behavior not because the language standard didn't define the behavior, but because the compiler isn't complying with the definition.
 

dl324

Joined Mar 30, 2015
18,449
But it doesn't work. I can compile an undefined array tempNameStore, but I can't use the sizeof operator after I have stored the value from scanf.
You haven't declared a size for tempNameStore.

You should have gotten an error something like this:
Code:
931 aac> cc -o t1 t1.c
t1.c: In function 'main':
t1.c:7:8: error: array size missing in 'tempNameStore'
   char tempNameStore[];
        ^
If your objective is to detect input that overflows the input buffer, you can use fgets() and see if the last character read was a newline.

In your original case, you were expecting the name of a month to be input. Since the names of months should be less than a dozen characters in any language, you could simply make the buffer large enough to hold the longest valid month and read it with fgets(); then validate it by checking to see if it's valid.

This started out as asking why you could enter more characters than you allocated. The answer was that it was causing a memory overwrite and some of the data overwrote an "unimportant" portion of memory. I suggested that you swap the order of the declared variables to increase the chance that you'd do a memory overwrite that the OS could detect and cause the program to abort with a segmentation violation. Then the thread digressed and became worthless to you.

EDIT: code in question so the line numbers make sense:
Code:
#include <stdio.h>

#define NAMELENGTH 7

main() {
  char name[8];
  char tempNameStore[];

  printf("Say my name, say my name..\n");
  scanf("%s,\n", &tempNameStore);

  if (sizeof(tempNameStore) <= sizeof(name)) {
    printf("You entered a correct name length, ");
    printf("and thus did not cause an overflow.\n");
    printf("%s", tempNameStore);
  } else {
    printf("No good.");
  }
  return 0;
}
 
Last edited:

MrSoftware

Joined Oct 29, 2013
2,273
I'm not familiar with CodeBlocks either, but if you want to use a reasonably good compiler that makes it easy to get your hands dirty with the lower level stuff, check out gcc. Likewise gdb is a good command line debugger. If you prefer a nice GUI IDE, Microsoft Visual Studio is the best one I've ever used, and I believe there is a free version. The visual debugger is fantastic and makes it easy to view memory, look into your structures, step through code, jump around in your code while debugging, sometimes even edit code, recompile and continue debugging without exiting the process after making code edits, etc.. With a good compiler/debugger, you'll spend more time learning the language and less time scratching your head over compiler or debugger bugs or inadequacies.
 

WBahn

Joined Mar 31, 2012
33,076
This started out as asking why you could enter more characters than you allocated. The answer was that it was causing a memory overwrite and some of the data overwrote an "unimportant" portion of memory. I suggested that you swap the order of the declared variables to increase the chance that you'd do a memory overwrite that the OS could detect and cause the program to abort with a segmentation violation.
This almost never happens. The OS can only detect such an error if the program attempts to access memory outside the bounds of the sandbox that the OS has given the program to play within and that is pretty hard to do if you are starting from a valid pointer within the program. Seg faults almost always happen when you dereference an invalid, effectively random pointer since a random pointer value has a high change of referring to memory outside the sandbox.

Overflowing a buffer's bounds almost always causes the corruption of memory that belongs to the program and the OS simply doesn't care what the program does with its own memory.
 

MrSoftware

Joined Oct 29, 2013
2,273
Going only by my own experience, anecdotally Windows seems to care less about minor buffer overruns than Linux. Linux was more likely to crash the process and Windows was more likely to allow the process to keep running, leading to some fun debug sessions trying to figure out where that 1 or 2 bytes was being corrupted. I was using different compilers and the settings surely were not identical so maybe that was part of the difference.
 

dl324

Joined Mar 30, 2015
18,449
This almost never happens.
Worked for my test.

Code:
#include <stdio.h>
#include <stdlib.h>

main() {
  char something[5];
  char month[] = "Oktober";

  printf("What month is it? %s ofc. \n", month);
  printf("Which is your favorite month?\n");
  scanf("%s", something);
  printf("Computing\n\n");
  printf("Your favorite month is %s\n", something);
}
Code:
aac> m2
What month is it? Oktober ofc.
Which is your favorite month?
asdfasdfasdfasdf
Computing

Your favorite month is asdfasdfasdfasdf
Segmentation fault
 
Last edited:

WBahn

Joined Mar 31, 2012
33,076
Worked for my test.

Code:
#include <stdio.h>
#include <stdlib.h>

main() {
  char something[5];
  char month[] = "Oktober";

  printf("What month is it? %s ofc. \n", month);
  printf("Which is your favorite month?\n");
  scanf("%s", something);
  printf("Computing\n\n");
  printf("Your favorite month is %s\n", something);
}
Code:
aac> m2
What month is it? Oktober ofc.
Which is your favorite month?
asdfasdfasdfasdf
Computing

Your favorite month is asdfasdfasdfasdf
Segmentation fault
If it had segfaulted because of writing beyond the buffer, it would have done so at the point the buffer was overrun, which is at the scanf() call. It would have never made it to the following printf() statement.

The buffer overrun went undetected. The OS didn't care since only memory that already belonged to the program was accessed.

But your buffer overrun corrupted other memory within the program and that corruption caused problems later on. Here is my best guess.

Since runtime stacks traditionally start in high memory and grow downward, the array 'something' is located near the beginning of the stack frame. Since arrays are indexed upward in memory, when you overran the buffer you walked into the area of the stack that is used by main()'s stackframe for storing such things as the return address and corrupted it. When your program then hit the end of the main() it returned to the calling function, which is probably some initialization/finalization code generated by the compiler, and so control tried to return to an address that has been corrupted and which then turns out to be outside the sandbox laid down by the OS.
 
Top