0

I am currently learning C and I am working on a task to improve some C-Code that might lead to a program crash.

Here is the code:

int main()
{
    // Define buffers to store username and password
    char username[16];
    char password[16];

    // Read username and password from user input
    printf("Enter your name: ");
    scanf("%s", username);
    printf("Enter your password: ");
    scanf("%s", password);
    printf("[SHOUTING OUT LOUD] Hello, %s!\n", username);

    return 0;
}

How can I make sure, that the input is not longer than 15 chars? Otherwise, the program could accidentally print out the password or overwrite the return address on the stack, which was subject of my other question:

Is it possible to crash this program somehow?

I already thought about putting the variables on the heap, but in the beginning, I don't know how long the input is. So I don't know, how much space I shall allocate.

Can somebody help me with this? Thanks :)

trincot
  • 317,000
  • 35
  • 244
  • 286
Mr. Moose
  • 101
  • 8
  • Does this answer your question? [Read no more than size of string with scanf()](https://stackoverflow.com/questions/12306591/read-no-more-than-size-of-string-with-scanf) – vgru Jun 08 '20 at 09:48
  • 1
    `fread(....,stdin);` or `fgets(...,stdin);` will read a specified number of characters but it doesn't stop the user from typing more characters. What do you do with all the extra characters they typed? – Jerry Jeremiah Jun 08 '20 at 09:50
  • @user3121023 Note that `getline` is a POSIX function and is not generally available outside POSIX systems. – Some programmer dude Jun 08 '20 at 10:03
  • You could read character by character until you reach `EOF`, a newline, or the limit of your buffer (15 in your example). Then terminate your buffer if you reached the limit, then continue to read until you reach read `EOF` or a newline. – Some programmer dude Jun 08 '20 at 10:05
  • 1
    **Read more about C programming**, in particular the [*Modern C*](http://modernc.gforge.inria.fr/) book and [this C reference](https://en.cppreference.com/w/c) and the documentation of your C compiler (e.g. [GCC](http://gcc.gnu.org/) ....) and of your debugger (e.g. [GDB](https://www.gnu.org/software/gdb/)). Enable all warnings and debug info, so compile with `gcc -Wall -Wextra -g` if you use [GCC](http://gcc.gnu.org/) – Basile Starynkevitch Jun 08 '20 at 10:28

1 Answers1

4

Use a length modifier with the amount of bufferlength - 1 for the %s conversion specifier.

In this case, as both arrays are consisted of 16 char elements, use %15s.

Note: Always check the return value of input functions, if an error occurred at consuming the input.

printf("Enter your name: ");
if ( scanf("%15s", username)) != 1 )
{
    fputs("Error at input - username!", stderr);
    exit(1);
}

// No explicit need for a call to getchar() to catch the newline from stdin       
// in your program. %s skips leading white space. 

printf("Enter your password: ");
if ( scanf("%15s", password)) != 1 )
{
    fputs("Error at input - password!", stderr);
    exit(1);
}

or use fgets() instead:

printf("Enter your name: ");
if ( fgets(username, sizeof(username), stdin) == NULL )
{
    fputs("Error at input - username!", stderr);
    exit(1);
}

// fgets can catch the newline with. If this is the case, we need or want to
// remove this newline from the string by replacing it with '\0'. 
// Else, we need to catch the left newline from stdin so that it is not   
// catched by fetching the password.


size_t len = sizeof(username);

for ( size_t i = 0; i < len; i++ )      // Iterate through the buffered string.
{
    if ( username[i] == '\n' )
    {
        username[i] = '\0';             // Replace newline with NUL.
        break;
    }

    if ( i == len - 1 )
    {
        getchar();                   // If no newline was in the string,    
    }                                // catch left newline from stdin.
}


printf("Enter your password: ");
if ( fgets(password, sizeof(password, stdin) == NULL )
{
    fputs("Error at input - password!", stderr);
    exit(1);
}

password[strcspn(password, "\n")] = '\0';    // Replace newline with NUL.

Note that although the latter use of fgets() seems more complicated, fgets() is general more safe than scanf() and the preferred choice to read strings.

Take a look at:

Disadvantages of scanf