0

i wrote a simple in/out program whenever i run it and enter the input and exceed the char limit i get *** stack smashing detected ***: terminated Aborted (core dumped)

i searched it up and found it was a gcc thing for safety,i heard it might lead to seg faults so i experimented turning it off with -fno-stack-protector and it ran normally if i exceeded the char limit

but what if i want to write the program if the input length is unknown, is there a safer way to do this? more efficient that increasing the value in char to an ridiculously large value?

the code:

#include <stdio.h>
int main()
{
        char in[1];
        printf("in: ");
        scanf("%s\0", &in);
        printf("\nout: %s\n", in);
}

P.s- im new to C, >2 days old so a simple explanation would be appreciated

umar
  • 25
  • 5
  • 1
    You said "it ran normally" but it didn't really. It acted as if it ran normally - you just got lucky. Undefined behaviour means exactly that - today it acted normally, tomorrow it might do something else. https://security.stackexchange.com/questions/258589/i-invoked-undefined-behaviour-do-i-need-a-new-computer – Jerry Jeremiah Apr 12 '22 at 01:38
  • umar, What do you want to happen if the input is more than the space available in `in[]`? – chux - Reinstate Monica Apr 12 '22 at 02:16
  • You can't use `%s` to read into `char in[1];` without overflowing the buffer. You can specify the number of characters that can be read into the string before the terminating null using the notation `%31s` to read into `char in[32];` (yes, the off-by-one is unfortunate but historically sanctified and unfixable now). But using `"%0s"` is pointless — and that's the only length that could be used with your `char in[1]` variable. (Incidentally, the `\0` in the format string is wholly superfluous — it's harmless but it indicates that the coder does not have full command of C yet.) – Jonathan Leffler Apr 12 '22 at 02:19
  • You could use `" %c"` to read a character into `char in[1];` but the result is a single character, not a string. Be cautious! The leading blank skips white space. The `%s` conversion specification does the skipping automatically; the `%c`, `%[…]` scan set and `%n` conversion specifications are the only ones that do not skip leading white space automatically. – Jonathan Leffler Apr 12 '22 at 02:24
  • @JonathanLeffler thanks, i wont be using `\0` moving on... – umar Apr 12 '22 at 02:49
  • Use a reasonably large buffer such as 128 bytes, then read input with `fgets` instead. – Lundin Apr 12 '22 at 09:48

2 Answers2

1

char in[1]; can hold only the empty string (a single null terminating byte), which is impossible to use safely with scanf.

Also note that explicitly stating the null terminating byte in a string literal is superfluous, as they are implicitly null terminated.

but what if i want to write the program if the input length is unknown, is there a safer way to do this? more efficient that increasing the value in char to an ridiculously large value?

The counter-questions here are:

  • What do you consider inefficient?
  • What do you define as ridiculously large?

As I see it, you have two options:

  1. Use dynamically allocated memory to read strings of an arbitrary size.
  2. Set a realistic upper limit on the length of of input to expect.

An example of #1 can be seen in library functions like POSIX getline or getdelim. Its re-implementation can be as simple as as malloc (realloc), getchar, and a loop.

The use of #2 depends greatly on the context of your program, and what it is supposed to do. Perhaps you a reading a single line, and a smallish buffer will suffice. Maybe you are expecting a larger chunk of data, and need more memory. Only you can decide this for yourself.

In any case, its up to you to avoid undefined behavior by preventing overflows before they happen. It is already too late if one has occurred.

Use field-width specifiers when using %s:

char buf[512];
if (1 != scanf("%511s", buf))
    /* read error */;

or use sane functions like fgets, which allow you to pass a buffer size as an argument.

Oka
  • 23,367
  • 6
  • 42
  • 53
  • will changing the string length affect memory consumption regardless if the input is two letter or a hundred? – umar Apr 12 '22 at 02:45
  • 1
    @umar So a quick clarification on terminology: a *string* is a sequence of non-null bytes terminated by a null byte. A string's *length* is determined by that sequence length, excluding the null byte. A string is *data*, not *memory*. You need memory to *store* data. So a declaration like `char buffer[32];` always *allocates* 32 bytes of usable memory (there may be more unusable memory for padding / alignment), enough to store a string of **up to** length 31 (we need room for the null byte of course). Storing something cannot affect memory consumption, only (de)allocating memory can. – Oka Apr 12 '22 at 02:59
0

stack smashing detected
i searched it up and found it was a gcc thing for safety

That's indeed gcc's way of spotting run-time bugs in your code by inserting so-called "stack canaries" to spot stack corruption/overflows. More errors detected is a good thing.

i heard it might lead to seg faults

No, bugs in your application lead to seg faults. If the compiler provides ways to detect them before the OS, that's a good thing. Dormant but severe bugs in your program is a bad thing. However, the OS would possibly detect the bug too and say "seg fault".

so i experimented turning it off with -fno-stack-protector and it ran normally if i exceeded the char limit

Basically you know that you are an inexperienced driver and afraid you might hit other cars. To solve, this you drive with your eyes closed instead, so you won't see those cars you could hit. That doesn't mean that they disappear.

char in[1]; can only hold 1 byte of data and if you read out of bounds of this array, you invoke undefined behavior, which will manifest itself as stack smashing or seg faults. Because you are trying to write to memory that doesn't belong to you. This is the bug, this is the problem. The correct solution is to allocate enough memory.

(You also have a bug scanf("%s\0", &in); -> scanf("%s\0", in);. The & isn't needed since in is an array and automatically "decays" into a pointer to its first element when you pass it to a function.)

One sensible way is to allocate 128 bytes or so, and then restrict the input so that it cannot be more than 128 bytes. The proper function to read strings with restricted input length is fgets. So you could either switch to fgets or you could accept that your beginner trial programs need not live up to production quality and just use scanf for now. (You can use scanf safely as shown in another answer, but IMO that's just more cumbersome than using fgets.)

Also I would strongly advise C beginners not to worry about if they allocate 10 bytes or 100 bytes. Learn programming by using a PC and then it won't matter. Optimizing memory consumption is an advanced topic which you will learn later on.

Lundin
  • 195,001
  • 40
  • 254
  • 396