On Sat, Dec 05, 2015 at 01:25:10PM -0500, Ted Unangst wrote:
> Tobias Stoeckmann wrote:
> >
> > And I still think that the current code is a bit too permissive in parsing
> > things. I mean what's the point in allowing lines like:
> >
> > sometextwithoutspace/bin/ksh should be used for logins # seriously!
> >
> > Which would result in /bin/ksh, by the way.
> >
> > Didn't notice the consequences that arise by keeping the descriptor open,
> > so I'm fine with an alternative approach. Yet we might make the code a
> > bit easier to review by not allowing such weird lines. What it should
> > expect and enforce:
> >
> > - a valid line has to start with a slash
> > - comments are chopped off
> > - comments are supposed to be at the beginning of a line
> >
> > So if somebody writes "/bin/ksh # comment", that actually leads to
> > "/bin/ksh ",
> > with an additional whitespace at the end. Currently we couldn't even
> > specify a
> > shell with a whitespace in its path.
>
> ok. i was going to leave the behavior alone, but we can fix that too.
>
> - use getline to read lines of any length.
> - only consider lines that start with a /.
> - truncate lines after a #, but not after spaces.
>
>
> Index: gen/getusershell.c
> ===================================================================
> RCS file: /cvs/src/lib/libc/gen/getusershell.c,v
> retrieving revision 1.16
> diff -u -p -r1.16 getusershell.c
> --- gen/getusershell.c 14 Sep 2015 16:09:13 -0000 1.16
> +++ gen/getusershell.c 5 Dec 2015 18:24:33 -0000
> @@ -28,14 +28,13 @@
> * SUCH DAMAGE.
> */
>
> -#include <sys/stat.h>
> -
> #include <ctype.h>
> #include <limits.h>
> #include <paths.h>
> #include <stdint.h>
> #include <stdio.h>
> #include <stdlib.h>
> +#include <string.h>
> #include <unistd.h>
>
> /*
> @@ -44,7 +43,7 @@
> */
>
> static char *okshells[] = { _PATH_BSHELL, _PATH_CSHELL, _PATH_KSHELL, NULL };
> -static char **curshell, **shells, *strings;
> +static char **curshell, **shells;
> static char **initshells(void);
>
> /*
> @@ -66,11 +65,14 @@ getusershell(void)
> void
> endusershell(void)
> {
> -
> + char **s;
> +
> + if ((s = shells))
> + while (*s)
> + free(*s++);
> free(shells);
> shells = NULL;
> - free(strings);
> - strings = NULL;
> +
> curshell = NULL;
> }
>
> @@ -84,48 +86,50 @@ setusershell(void)
> static char **
> initshells(void)
> {
> - char **sp, *cp;
> + size_t nshells, nalloc, linesize;
> + char *line;
> FILE *fp;
> - struct stat statb;
>
> free(shells);
> shells = NULL;
> - free(strings);
> - strings = NULL;
> +
> if ((fp = fopen(_PATH_SHELLS, "re")) == NULL)
> return (okshells);
> - if (fstat(fileno(fp), &statb) == -1) {
> - (void)fclose(fp);
> - return (okshells);
> - }
> - if (statb.st_size > SIZE_MAX) {
> - (void)fclose(fp);
> - return (okshells);
> - }
> - if ((strings = malloc((size_t)statb.st_size)) == NULL) {
> - (void)fclose(fp);
> - return (okshells);
> - }
> - shells = calloc((size_t)(statb.st_size / 3 + 2), sizeof (char *));
> - if (shells == NULL) {
> - (void)fclose(fp);
> - free(strings);
> - strings = NULL;
> - return (okshells);
> - }
> - sp = shells;
> - cp = strings;
> - while (fgets(cp, PATH_MAX + 1, fp) != NULL) {
> - while (*cp != '#' && *cp != '/' && *cp != '\0')
> - cp++;
> - if (*cp == '#' || *cp == '\0')
> +
> + line = NULL;
> + nalloc = 10; // just an initial guess
> + nshells = 0;
> + shells = reallocarray(NULL, nalloc, sizeof (char *));
> + if (shells == NULL)
> + goto fail;
> + linesize = 0;
> + while (getline(&line, &linesize, fp) != -1) {
> + if (*line != '/')
> continue;
> - *sp++ = cp;
> - while (!isspace((unsigned char)*cp) && *cp != '#' && *cp !=
> '\0')
> - cp++;
> - *cp++ = '\0';
> + line[strcspn(line, "#\n")] = '\0';
> + if (!(shells[nshells] = strdup(line)))
> + goto fail;
> +
> + nshells++;
> + if (nshells == nalloc) {
> + char **new = reallocarray(shells, nalloc * 2,
> sizeof(char *));
> + if (!new)
> + goto fail;
This 'goto fail' will free() beyond allocated: shells[nshells--]
Better to check 'if (nshells + 1 == nalloc)' and increment nshells
afterward.
--patrick
> + shells = new;
> + nalloc *= 2;
> + }
> }
> - *sp = NULL;
> + free(line);
> + shells[nshells] = NULL;
> (void)fclose(fp);
> return (shells);
> +
> +fail:
> + free(line);
> + while (nshells)
> + free(shells[nshells--]);
> + free(shells);
> + shells = NULL;
> + (void)fclose(fp);
> + return (okshells);
> }
>