WSL icon indicating copy to clipboard operation
WSL copied to clipboard

`int getlogin_r(char *buf, size_t len)` broken

Open isavegas opened this issue 9 years ago • 9 comments

Please use the following bug reporting template to help produce actionable and reproducible issues:

  • A brief description Attempting to use the function to get the login id of the user running the executable returns garbled strings, with a different return value each time.
  • Expected results
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(){
        size_t len = 10;
        char buf[len];
        size_t ret = getlogin_r(buf, len);
        puts(buf);
        return 0;
}

should print out my username, with ret set to 0 to indicate success.

  • Actual results (with terminal output if applicable)

The value of ret is 2, indicating an error. Depending on the C compiler used, it outputs garbage strings. If compiled with GCC or Clang, it prints out a single "unknown char" box. If compiled with TCC, it prints out a string the size of one's username, composed of aforementioned boxes and random numbers, letters, and symbols.

  • Your Windows build number

14393

  • Steps / commands required to reproduce the error

gcc test.c -o test && ./test

  • Strace of the failing command

Command doesn't fail in this sense.

  • Required packages and commands to install

sudo apt-get install build-essential

image

isavegas avatar Aug 13 '16 20:08 isavegas

Can confirm that the above sample doesn't work on WSL. Tried on a personal Ubuntu Linux Server box, it worked. Tried on WSL, and it didn't. I would also like to note that ret is set to 0 on linux, and 2 on WSL.

TheWhoAreYouPerson avatar Aug 13 '16 21:08 TheWhoAreYouPerson

Compiling the program on a build slave (Ubuntu 15.10) and transferring it to WSL results in the same garbage output. It works just fine on aforementioned Ubuntu instance.

image

isavegas avatar Aug 13 '16 21:08 isavegas

It's in the strace. WSL has no /proc/self/loginuid.

open("/proc/self/loginuid", O_RDONLY)   = -1 ENOENT (No such file or directory)

therealkenc avatar Aug 13 '16 23:08 therealkenc

WSL doesn't have /proc/self/loginuid as mentioned above, but also doesn't have /var/run/utmp, to which getlogin_r(3) will fall back in the absence of the former. This is all part-in-parcel of #352 and #573. As a workaround in the absence of those two issues being addressed:

$ sudo touch /var/run/utmp

Then:

C:\ wsl.exe sudo login -f someuser

The test case in the Summer 2016 OP is basically equivalent to logname(1), which will now succeed.

$ logname
ken

therealkenc avatar Jun 01 '18 10:06 therealkenc

I still have this issue. The thing is, it returns \0 for debug "-O0" builds and some garbage value for others.

EDIT: Nope. So it looks like in other systems where getlogin_r() is functioning, it writes the first few bytes with the username string. On WSL where this is not working, nothing is written. So, when the buffer is uninitialized and corrupted, it returns a garbage value. However, this is not an error condition according to the Manual page [getlogin_r(3)], so it returns success. I'm deciding to overwrite the first character of the buffer with '\0' where bufsize > 0, when the username is not available. Unfortunately getlogin_r(3) doesn't return the number of characters written, so this is tricky for me

ghost avatar Oct 06 '20 03:10 ghost

The thing is, it returns \0 for debug "-O0" builds and some garbage value for others.

The contents of the buffer passed doesn't matter (initialized or otherwise). As was mentioned in the OP, on WSL getlogin_r() returns 2 (ie ENOENT). In practice you'd test !=0. In an abundance of caution, you can also test against ERANGE. Looks like:

int main() {
    char buf[256];
    if (getlogin_r(buf, sizeof(buf)) != 0) {
        strncpy(buf, "<unknown>", sizeof(buf));
        // or handle the error however you want
    }
    printf("user is: %s\n", buf);
    return 0;
}

therealkenc avatar Oct 06 '20 05:10 therealkenc

The thing is, it returns \0 for debug "-O0" builds and some garbage value for others.

The contents of the buffer passed doesn't matter (initialized or otherwise). As was mentioned in the OP, on WSL getlogin_r() returns 2 (ie ENOENT). In practice you'd test !=0. In an abundance of caution, you can also test against ERANGE. Looks like:

int main() {
    char buf[256];
    if (getlogin_r(buf, sizeof(buf)) != 0) {
        strncpy(buf, "<unknown>", sizeof(buf));
        // or handle the error however you want
    }
    printf("user is: %s\n", buf);
    return 0;
}

Thank you, @therealkenc . It does say it returns "nonzero" instead of -1 for error.

ghost avatar Oct 06 '20 06:10 ghost

Still somewhat broken for me. I found this issue while using the python function os.getlogin()

The first wsl call that boots the distro opens a proper login shell and create the /var/run/utmp entries.

Every following wsl call directly opens a shell without being connected to a login session. You can start a new login session with simply sudo login that will make the proper entry in the utmp file.

first powershell window

PS C:\Users\bigpet> wsl
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.146.1-microsoft-standard-WSL2 x86_64)
bigpet@Ubuntu:~$ python -c "import os; print(os.getlogin())"
bigpet

in another powershell window

PS C:\Users\bigpet> wsl
bigpet@Ubuntu:~$ python -c "import os; print(os.getlogin())"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
OSError: [Errno 6] No such device or address
bigpet@Ubuntu:~$ sudo login
[sudo] password for bigpet:
Ubuntu login: bigpet
Password:
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.146.1-microsoft-standard-WSL2 x86_64)
bigpet@Ubuntu:~$ python -c "import os; print(os.getlogin())"
bigpet

Bigpet avatar Mar 31 '25 19:03 Bigpet

From a quick glance at the code it looks like the culprit might be this piece of code that creates the login only for the first time a uid creates a process:

https://github.com/microsoft/WSL/blob/1cea24b5bff2bb14e8cb6c2e26e138eb8ac220e0/src/linux/init/config.cpp#L429-L432

But that's mostly a wild guess. Might be completely unrelated

edit: looks like it's slightly related, but not the solution. The code after that does the actual login in a pty that it keeps open. But the pty connected to the terminal is then another one, not directly connected to the terminal presented by wsl.

But my testing is possibly not done with the same version of the code that I'm looking at in this repo

PS C:\Users\bigpet> wsl
Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.146.1-microsoft-standard-WSL2 x86_64)
This message is shown once a day. To disable it please create the
/home/bigpet/.hushlogin file.


bigpet@myhost:/mnt/c/Users/bigpet$ python3 -c "import os; print(os.getlogin())"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
OSError: [Errno 6] No such device or address


bigpet@myhost:/mnt/c/Users/bigpet$ tty
/dev/pts/0


bigpet@myhost:/mnt/c/Users/bigpet$ who -u
bigpet   pts/1        2025-05-21 07:37   .          1105


bigpet@myhost:/mnt/c/Users/bigpet$ exit
logout


PS C:\Users\bigpet> wsl --version
WSL version: 2.1.5.0
Kernel version: 5.15.146.1-2
WSLg version: 1.0.60
MSRDC version: 1.2.5105
Direct3D version: 1.611.1-81528511
DXCore version: 10.0.25131.1002-220531-1700.rs-onecore-base2-hyp
Windows version: 10.0.19045.5737

Bigpet avatar May 20 '25 16:05 Bigpet