[Pkg-shadow-devel] Bug#505071: login tty mis-determination (see bug#332198)

Paul Szabo psz at maths.usyd.edu.au
Fri Jan 23 02:10:24 UTC 2009

We had discussed whether login fails often due to left-over utmp
entries. I guess that depends on how likely it is that processes die
without cleanup; and how important it is that login should "work".

I now see that there is the possibility for a DoS attack, by filling
up utmp with left-over entries to cover all PIDs (or a significant
proportion of PID space). For example, we can cause one left-over
entry with

  run xterm and within that xterm use "kill -9 $PPID"

and might try that repeatedly to exhaust PID space; except xterm
reuses ptys and re-writes utmp entries, and the pty space is smaller
than PID space...

Until this is fixed, utmp should be sanitized regularly to remove
unused entries. I now use the following, often (i.e. nightly).


Paul Szabo   psz at maths.usyd.edu.au   http://www.maths.usyd.edu.au/u/psz/
School of Mathematics and Statistics   University of Sydney    Australia


#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <utmp.h>
#include <time.h>
#include <signal.h>
#include <errno.h>

int main( int argc, char *argv[])
char *usage = "Usage:\n\n\
  clear-dead-utmp-entries [-quiet] [-really]\n\n\
to clear left-over utmp entries.\n\n";

int debug = 0;
int quiet = 0;
int really = 0;
int cleared = 0;
int clearable = 0;
int header = 0;
int i;
struct utmp *utmpp;
struct tm *tmmp;
#define buflen 256
char buf[buflen];
struct stat strst;

for (i=1; i<argc; i++) {
  if (!strcmp(argv[i], "-debug")) {
  else if (!strcmp(argv[i], "-quiet")) {
  else if (!strcmp(argv[i], "-really")) {
  else {
    printf ("\nBad option %s. %s", argv[i], usage);
    return 1;


while (utmpp = getutent()) {
  if (utmpp->ut_type != INIT_PROCESS &&
      utmpp->ut_type != LOGIN_PROCESS &&
      utmpp->ut_type != USER_PROCESS) continue;
  if (utmpp->ut_pid == 0) continue;

  /* Is this PID "alive"? */
  /* Seems that /usr/bin/who uses
  if (kill(utmpp->ut_pid, 0) < 0 && errno == ESRCH) continue;
     but "plain" psz gets EPERM, while root gets success with ENOENT.
  sprintf(buf, "/proc/%d", (int)utmpp->ut_pid);
  if (!stat(buf, &strst)) continue;

  tmmp = localtime(&utmpp->ut_time);
  strftime(buf, buflen, "%b %d %H:%M", tmmp);
  if (debug) {
    printf ("\nutmp line:\n");
    printf ("  ut_user: %.*s\n", sizeof(utmpp->ut_user), utmpp->ut_user);
    printf ("  ut_id:   %.*s\n", sizeof(utmpp->ut_id), utmpp->ut_id);
    printf ("  ut_line: %.*s\n", sizeof(utmpp->ut_line), utmpp->ut_line);
    printf ("  ut_type: %d\n", (int)utmpp->ut_type);
    printf ("  ut_pid:  %d\n", (int)utmpp->ut_pid);
    printf ("  e_term:  %d\n", (int)utmpp->ut_exit.e_termination);
    printf ("  e_exit:  %d\n", (int)utmpp->ut_exit.e_exit);
    printf ("  ut_time: %s\n", buf);
    printf ("  ut_host: %.*s\n", sizeof(utmpp->ut_host), utmpp->ut_host);
    printf ("\n");

  if (!header) {
    printf ("T PID   Name      Line    Time         Hostname\n");
  printf ("%-2d%-6d%-10.10s%-8.8s%-13.13s%.40s\n",

  if (!really) {

  utmpp->ut_type = DEAD_PROCESS;
  if (! pututline(utmpp)) {
    printf ("\nERROR: Cannot clear utmp entry above\n\n");

if (cleared==1) {
  printf ("Cleared one utmp entry shown above\n");
else if (cleared>1) {
  printf ("Cleared %d utmp entries shown above\n", cleared);
else {
  if (clearable) printf ("No utmp entries cleared, need '-really' option for that\n");
  else if (!quiet) printf ("No utmp entries cleared\n");


More information about the Pkg-shadow-devel mailing list