-3

I came across a strange behaviour of C with while loop today.

A very simple code :

int userLogin()
{
    struct User user;
       
    while (1)
    {
        printf("\n\nEnter username (or quit to return back) - ");
        char userName[50];
        scanf("%s", userName);

        if (strcmp(userName, "quit") == 0)
        {
            printf("\nReturning to Login menu...\n");
            return 2;
        }

        printf("\n\nEnter Password - ");
        char password[50];
        scanf("%s", password);

        user = login_user(userName, password);
        if (strcmp(user.userID, userName) != 0 ||
            strcmp(user.password, password) != 0)
        {
            printf("\nInvalid username or password !\n");
        }
        else
        {
            printf("\nLogin successful...Entering Library menu !\n");
            currentUser = user;

            if (currentUser.role == 1)
            {
                libraryOptions();
            }
            else
            {
                adminPanel();
            }

            break;
        }
    }
}

Now this gives out Segmentation fault (Core Dumped).

enter image description here

However, same code, I just insert a print statement before while(1) :

enter image description here

And suddenly it starts working...

enter image description here

Can somebody help me understand the reason for this ? What am I missing here ?

PS: I know what segmentation fault is, what I need to understand is what changes if I include a print statement.

PPS: struct User is in a header file

struct User {
        char *userName; //User name
        char *userEmail; //Usre email
        char *userID; // user id
        char *password; //password
        int role; //1 for regular user and 2 for admin
};

login_user is also in another C file which includes the user header file:

 struct User login_user(const char *userName, const char *password)
{
   

 int i;
    struct User user;
    user.userName = "";
    user.password="";

    if (allUsers.length == 0)
    {
        return user;
    }

    for (i = 0; i < allUsers.length; i++)
    {
        if (strcmp(userArray[i].userID, userName) == 0 &&
            strcmp(userArray[i].password, password) == 0)
        {
            user.userName = userArray[i].userName;
            user.password = userArray[i].password;
            user.userEmail = userArray[i].userEmail;
            user.userID = userArray[i].userID;
            user.role = userArray[i].role;
            return user;
            
        }

      
    }
    return user;
    
}

I still reiterate that I don't want the answer to main code or reason to segmentation fault (I am working on it), all I request is someone to explain the reason why the print statement makes the code work.

  • 4
    What is `struct User`? Please post a [Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example). – MikeCAT Feb 23 '21 at 08:14
  • Guessed as duplicate: [c - Crash or "segmentation fault" when data is copied/scanned/read to an uninitialized pointer - Stack Overflow](https://stackoverflow.com/questions/37549594/crash-or-segmentation-fault-when-data-is-copied-scanned-read-to-an-uninitializ) – MikeCAT Feb 23 '21 at 08:14
  • 1
    @MikeCAT I know what segmentation fault is, what i need to know what changes in case i use a print statement –  Feb 23 '21 at 08:16
  • 3
    Change in behavior by code change which don't seem to cause change is suggesting that you are invoking *undefined behavior*. – MikeCAT Feb 23 '21 at 08:17
  • 2
    Have you tried to catch the crash with a debugger to see exactly when and where it happens in your code? – Some programmer dude Feb 23 '21 at 08:20
  • 1
    Also the function `login_user` is not disclosed. – MikeCAT Feb 23 '21 at 08:22
  • 1
    In simple terms: if you access non-allocated memory. You can be lucky or not. In the version without `printf` you are not lucky, and in the version with `printf` you are lucky. BUT that is NOT FOR SURE. Change anything else, like code, computer, or compiler option, and you can be unlucky again. Whole code needs to be checked. Show `struct User`. Show where it breaks about: `user = login_user(userName, password);` (I saw similar things before, even with `printf`, and I guess, many others did). – sidcoder Feb 23 '21 at 08:59
  • 1
    Added all the info asked –  Feb 23 '21 at 09:17
  • If `userArray` is an array of `struct User` then why not simply `return `userArray[i];`? And anyway, how do you initialize this `userArray`? And you seem to have quite a few global variables, which isn't a sign of good code. – Some programmer dude Feb 23 '21 at 09:20
  • 1
    @Someprogrammerdude I was returning userArray[i] only, this time was testing the reason for Segmentation fault when i pasted the code. The reason for global variables is the requirement of code as given. Also i never wanted the solution to the main code, all I was interested in was why the print statement made it work ! –  Feb 23 '21 at 09:23
  • You say that you want someone to "explain the reason why the print statement makes the code work". The problem is that we *can't*. If you have [*undefined behavior*](https://en.wikipedia.org/wiki/Undefined_behavior) anything could make the code crash, or work, or [summon nasal demons](http://catb.org/jargon/html/N/nasal-demons.html), there's simply no way of saying what will happen beforehand. – Some programmer dude Feb 23 '21 at 09:30

2 Answers2

1

Nobody can explain why you encounter this issue as this is undefined behavior. You must fix your code instead of trying to understand something that cannot be understood.

In your login_user function you are allocating strings like this :

struct User user;
user.userName = "";
user.password="";

And then return the user. You cannot do that. This allocates a nul terminated string on the stack but is "destroyed" when the function is done. Maybe what you meant to do is = NULL ?

Use fixed size arrays in your struct instead :

#define MAX_BUFFER_SIZE 100

struct User {
    char userName[MAX_BUFFER_SIZE]; //User name
    char userEmail[MAX_BUFFER_SIZE]; //Usre email
    char userID[MAX_BUFFER_SIZE]; // user id
    char password[MAX_BUFFER_SIZE]; //password
    int role; //1 for regular user and 2 for admin
};

Also, as someone else pointed out in his answer, scanf functions used like you did can cause more undefined behavior as a buffer overflow could happen. There are ways to use scanf in a safe way but I suggest you take a look at fgets. The same applies to strcmp, you should use strncmp instead.

ShellCode
  • 1,072
  • 8
  • 17
  • 1
    Thanks, but as said in main question, I stumbled upon the unique behavior of while loop & thought if someone could make me understand that.. –  Feb 23 '21 at 09:41
  • The issue you encountered is called [undefined behavior](https://en.wikipedia.org/wiki/Undefined_behavior) it has no explanation whatsoever. Just fix your code – ShellCode Feb 23 '21 at 09:43
0
        char userName[50];
        scanf("%s", userName);

Very bad, you do not control length in input string

        char password[50];
        scanf("%s", password);

same here. Also, you do not change echoed symbols to "*".

        if (strcmp(user.userID, userName) != 0 ||
            strcmp(user.password, password) != 0)

You have not allocated memory for user.userID and user.password strings.

Also, after authentication, you have to fill password string with zeros so it will not remain in RAM

user3431635
  • 372
  • 1
  • 7
  • 1
    "You have not allocated memory for user.userID and user.password strings." We don't know that. – Some programmer dude Feb 23 '21 at 09:34
  • 1
    Please read the main question again, I stumbled upon the unique behavior of while loop & thought if someone could make me understand that.. –  Feb 23 '21 at 09:40
  • If you have random data in pointers (if this is the case), you will get unpredicted behavior. You are adding some code and it changes what is located where in RAM when a program executes. – user3431635 Feb 23 '21 at 10:56