cexceptionstackruntime-errorcorrupted-state-exception

Can someone explain to me why i am getting an exception thrown with this code


My code is supposed to scan a username and password and make sure that they aren't greater than the max amount. If they are, it should re-prompt the user to enter something.

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <string.h>

#define MAXNAME 30

int main() {
    char username_input[MAXNAME];
    char password_input[MAXNAME];

    username_input[MAXNAME - 1] = '\0';
    password_input[MAXNAME - 1] = '\0';

    printf("Please enter your username: ");
    while (scanf("%s", username_input) != 1 || strlen(username_input) > MAXNAME) {
        printf("Improper input.\nPlease enter your username: ");
    }

    printf("Please enter your password: ");
    while (scanf("%s", password_input) != 1 || strlen(password_input) > MAXNAME) {
        printf("Improper input.\nPlease enter your password: ");
    }
    return 0;
}

The problem that I am facing is that whenever one of these while loops are entered, I will get the re-prompt fine and I will be able to continue with the code, but once i hit return 0 I will have an exception thrown.

The exception reads: Run-Time Check Failure #2 - Stack around the variable 'username_input' was corrupted.

I have tried using scanf_s and fgets() as well, those haven't seemed to work.


Solution

  • Try this:

    #include <stdio.h>
    #include <stdbool.h>
    #include <string.h>
    
    #define MAX_LEN 30
    
    void read_ident(char const * descr, char buf[MAX_LEN + 2])
    {
        do {
            printf("Please enter your %s: ", descr);
            if (scanf("%31[^\n]", buf) == 1 && strlen(buf) <= MAX_LEN) {
                break;
            }
            printf("Improper input.\n");
            scanf("%*[^\n]");
            scanf("%*c");
        } while (!feof(stdin));
        scanf("%*c");
    }
    
    int main()
    {
        char username_input[MAX_LEN + 2], password_input[MAX_LEN + 2];
        read_ident("username", username_input);
        read_ident("password", password_input);
    }
    

    I love scanf, but you might find that fgets is more convenient. See reference.

    The idea of my code above is to read 31 characters at most, before the end of line. If you don’t get more than 30 characters you’re done. Otherwise, %*[^\n] skips all characters before the end of line (if any: if the end of line immediately follows, this attempt to read will fail, this is why we must have two consecutive scanf), then %*c skips the end of line character (“line feed”).

    If you don’t want to or cannot hard code the length of your buffer, you could write something like that:

    char format[16];
    sprintf("%%%d[^\n]", MAX_LEN + 1);
    scanf(format, buf);
    

    As said by @Retired Ninja, your code crashes because you don’t tell scanf how many characters it should read at most, so it may overflow your buffer. Reading strings with scanf shouldn’t normally be done without specifying a width (unless you don’t care about safety).

    Here is how to do with fgets:

    void read_ident(char const * descr, char buf[MAX_LEN + 2])
    {
        do {
            printf("Please enter your %s: ", descr);
            fgets(buf, MAX_LEN + 2, stdin);
            size_t nb_read_chars = strlen(buf);
            if (nb_read_chars >= 2 && buf[nb_read_chars - 1] == '\n') {
                buf[nb_read_chars - 1] = '\0';
                break;
            }
            printf("Improper input.\n");
            if (nb_read_chars >= 2) {
                while (fgetc(stdin) != '\n');
            }
        } while (!feof(stdin));
    }
    

    I do prefer scanf.

    scanf_s won’t help because they trigger an exception if there is an error, this is not what you want to do (and I think you will rarely want to do that).