This is the mail archive of the mailing list for the glibc project.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [patch] Fix BZ #18660 -- overflow in getusershell

On 18 Aug 2015 16:54, Paul Pluzhnikov wrote:
>  	while (fgets_unlocked(cp, flen - (cp - strings), fp) != NULL) {
>  		while (*cp != '#' && *cp != '/' && *cp != '\0')
>  			cp++;
> -		if (*cp == '#' || *cp == '\0' || cp[1] == '\0')
> +		/* Reject non-absolute paths, or anything too short.  */
> +		if (cp[0] != '/' || cp[1] == '\0' || isspace(cp[1]))
>  			continue;
>  		*sp++ = cp;
>  		while (!isspace(*cp) && *cp != '#' && *cp != '\0')
>  			cp++;
> +		assert (cp < strings + flen);
>  		*cp++ = '\0';
>  	}

i'm having a hard time seeing how this works sanely (even before your change).
considering this file starts off with this comment:
/* NB: we do not initialize okshells here.  The initialization needs
   relocations.  These interfaces are used so rarely that this is not
   justified.  Instead explicitly initialize the array when it is
   used.  */

how can we justify this ugly code in the name of speed ?  wouldn't it be nicer
to gut it completely and avoid the stat/large malloc/etc... ?

lightly tested from scratch version below.  code & data size is smaller than
the current version in the tree.

/* Copyright (C) 2015 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <>.  */

#include <assert.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/* Used by tests to point to custom paths.  */

/* These functions are not thread safe (by design), so globals are OK.  */
static FILE *shellfp;
static int okshell_idx;
static char *shellbuf;
static size_t buf_len;

char *
getusershell (void)
  /* Handle the builtin case first.

     NB: we do not use a global array here as the initialization needs
     relocations.  These interfaces are used so rarely that this is not
     justified.  Instead open code it with a switch statement.  */
  switch (okshell_idx)
    case 0:
      /* Require the user call setusershell first.  */
      assert (shellfp != NULL);
    case 1:
      return (char *) _PATH_BSHELL;
    case 2:
      return (char *) _PATH_CSHELL;
    case 3:
      return NULL;

  /* Walk the /etc/shells file.  */
  while (getline (&shellbuf, &buf_len, shellfp) != -1)
      char *p;

      /* Strip out any comment lines.  */
      p = strchr (shellbuf, '#');
      if (p)
	*p = '\0';
	  /* Chop the trailing newline.  */
	  p = strchr (shellbuf, '\n');
	  if (p)
	    *p = '\0';

      /* Only accept valid shells (which we define as starting with a '/').  */
      if (shellbuf[0] == '/')
	return shellbuf;

  /* No more valid shells.  */
  return NULL;

hidden_proto (endusershell);
endusershell (void)
  okshell_idx = 0;
  if (shellfp)
      fclose (shellfp);
      shellfp = NULL;
      free (shellbuf);
      shellbuf = NULL;
hidden_def (endusershell);

setusershell (void)
  /* We could rewind shellfp here, but we get smaller code this way.
     Keep in mind this is not a performance sensitive API.  */
  endusershell ();

  shellfp = fopen (_LOCAL_PATH_SHELLS, "rce");
  if (shellfp == NULL)
    okshell_idx = 1;

Attachment: signature.asc
Description: Digital signature

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]