As you are probably aware, KDE software uses the Qt library extensively. As do many other 3rd party applications.

Most of these applications use the Qt’s default text component – QLineEdit – when they need password input, because QLineEdit has a nice convenient mode where it masks the content of the text field – it shows asterisks or circles instead of the actual characters it contains.

This is a nice way to block over-the-shoulder snooping, and is a common approach to do password entry even in non-Qt software.

The underbelly

In order to see what the problem here is, we need to understand how components like QLineEdit work.

While the typed characters are not shown to the user, they are stored somewhere. Usually in a string variable as plain text (not encrypted). This means that is someone can see the contents of the process memory while the user types in the password, it will be easy to read it (problem 1).

Other problems are due to how dynamic memory works on our computers. String data is stored (usually) in the dynamic memory. When QString wants to store its data, it will reserve a chunk of memory and start filling it while the string grows (in this case, while the user is typing the password). When that chunk is filled, a new larger chunk is allocated and the data from the old chunk is copied into it. The old chunk is freed/deleted so that some future memory allocation can use the same space.

When the QLineEdit is destroyed, so is the QString variable that stores the password. But, while the buffer that QString uses to store the data is freed/deleted, its contents remain in memory until some other dynamically allocated object is created in the same memory space and overwrites the data. This is because QString does not fill its buffer with zeroes on destruction. This means that the passwords remain in memory for much longer than needed (problem 2).

The dynamic memory is like graffiti - the old content remains until somebody paints over it
The dynamic memory is like graffiti - the old content remains until somebody paints over it

Furthermore, because of string reallocation on resizing (creating a bigger chunk of memory and copying the old data into it), it is quite possible that, while the user is typing the password, that the string will be resized/reallocated at least once. This means that partial passwords (data from the buffer in the string before reallocation which is not zeroed-out) will remain in memory until something else overwrites them (problem 3).

All this memory can end up written to the HDD/SSD if the application is moved to swap by the OS (problem 4).

Small potatoes

Before you throw your computer into the fireplace, and go to the tried and tested coding systems of (c)old, we need to see how big these problems really are.

Reading passwords stored in the process memory can be done from that same process. This is usually benign because if you’re entering a password into an application, you probably trust it with said password. This can only become a problem if that application has a plugin system (with native code) and you install untrusted plugins.

This should not be a common case. Usually, privacy-aware people do not use untrusted software.

Reading memory from a separate process is a different story. This is not possible on a hardened system, but most Linux distributions tend to be developer-friendly, and this means that they allow attaching a debugger to a running process with PTRACE. And a debugger (or any process that pretends to be a debugger) can read the memory of the process it is attached to.

Regardless of whether PTRACE is enabled or not, for this to be a problem, you need to be running untrusted software that could try to extract the passwords from a process’ memory.

It is worth noting that even in the worst case scenario in which an attacker is allowed to read the memory of your processes either through a plugin or through PTRACE, finding the password (or other secret data) is like searching for a needle in a huge haystack. Normal programs tend to be filled with strings.

Furthermore, if an attacker is allowed any of this, your system is seriously compromised.

Current improvements

I’ve started this post explaining 4 different problems of using QLineEdit for password input. I’m glad to say that two of them do not exist anymore.

The latest version of Qt includes two patches (by yours truly) which remove the problem of passwords remaining in the dynamic memory of a process after the input component is destroyed. Namely, when QLineEdit is used for password input, it will fill the memory where the password was stored with zeros. Furthermore, it will preallocate a buffer for the password which is long enough so that string reallocations don’t happen for 99.99% of users.

This means that problems 2 and 3 are no longer present if you are using the latest version of Qt.

The fourth problem – the operating system moving the application that asks you for the password to the swap should also not happen in common usage. Since problems 2 and 3 are gone, the only time when the password is in memory is while you’re typing it and the OS is not going to swap an application that you are currently using.

If you don’t think this is secure enough, you can either disable the swap on your system or encrypt it.

The last problem

The last remaining problem is that the password is stored non-encrypted in memory. Luckily, thanks to the previously mentioned patches, this is a much smaller issue than it seems at first.

The password is in memory only while it is being entered/used. Which means that the window that the attacker has to try to get it is extremely narrow.

Unfortunately, this problem can not be fixed completely. If the password was kept in memory encrypted, the encryption key would need to be readily available (in memory) for the application to be able to use the password. Which means that while your password would be encrypted, the key to decrypt it wouldn’t.

So, the attacker could try to find the encryption key, then find the encrypted password, and then decrypt the password.

Now, this would be much harder to do than finding an unencrypted password. The attacker would need to find two separate things (located in different parts of the process memory) and would not be able to use any type of string analysis when searching for the chunks of memory where the secrets are stored. The encryption key would be a true random set of bytes, and the encrypted password would look like a random set of bytes – they would not look like strings at all.

Unfortunately, this is not something that can be fixed in QLineEdit. This will need a custom password edit component that uses a safe string implementation instead of QString.

To be continued…


You can support my work on Patreon, or you can get my book Functional Programming in C++ at Manning if you're into that sort of thing.