Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve allocations #649

Merged
merged 14 commits into from Feb 24, 2023
Merged

Improve allocations #649

merged 14 commits into from Feb 24, 2023

Conversation

alejandro-colomar
Copy link
Collaborator

No description provided.

@alejandro-colomar alejandro-colomar changed the title Improve allocation Improve allocations Feb 4, 2023
lib/malloc.h Fixed Show resolved Hide resolved
@alejandro-colomar
Copy link
Collaborator Author

Regarding the "high" report from CodeQL (Missing return-value check for a 'scanf'-like function):

Like what? That's exactly what we do. This analyzer be kidding us :)

@alejandro-colomar
Copy link
Collaborator Author

Rebased to master.

$ git range-diff gh/master..gh/alloc  master..alloc 
 1:  85b5ccaa <  -:  -------- Remove superfluous casts
 2:  b277bf98 <  -:  -------- ttytype(): Fix race
 3:  41cc3c4c =  1:  03aa642e Rely on realloc(NULL, ...) being equivalent to malloc(...)
 4:  6b5cb73a =  2:  b9355fd6 malloc(3) already sets errno to ENOMEM
 5:  2bc8c628 =  3:  fd163a61 Use reallocf(3) instead of its pattern
 6:  502221f2 =  4:  8fa33624 Use reallocarray(3) instead of its pattern
 7:  8011b006 =  5:  60e1bee6 Use calloc(3) instead of its pattern
 8:  b0fade29 =  6:  ebc30525 Add safer allocation functions
 9:  3e936455 =  7:  31471ecb Use xcalloc(3) instead of its pattern
10:  9630f967 =  8:  918a3957 Use the new header for xstrdup()
11:  ed5af2c5 =  9:  1676ebad Use *array() allocation functions where appropriate
12:  aa91c6a3 = 10:  f87ad6ef Use reallocarrayf() instead of its pattern
13:  759afd46 = 11:  6066d8fd Use xreallocarray() instead of its pattern
14:  c4820476 = 12:  e7416270 Add safer allocation macros
15:  06ea8fb8 = 13:  6eaf62e8 Use safer allocation macros
16:  f37f1470 = 14:  ea048a41 Fix use-after-free of pointer after realloc(3)

@alejandro-colomar alejandro-colomar marked this pull request as ready for review February 10, 2023 01:58
@ikerexxe
Copy link
Collaborator

Is this PR ready for review or are you planning to add more commits? I'd prefer to review everything in a row.

@alejandro-colomar
Copy link
Collaborator Author

alejandro-colomar commented Feb 16, 2023

Is this PR ready for review or are you planning to add more commits? I'd prefer to review everything in a row.

Yes, I think it's ready. No plans :)

Copy link
Collaborator

@ikerexxe ikerexxe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two major comments apart from the ones inline. The first one is related to the malloc.* files naming. As they contain more functionality apart from malloc I wonder if we should rename it to something more generic.

The second one is related to the new macros provided in e741627. Wouldn't it be better to add this functionality to the functions themselves? We are already adding an abstraction and I'd prefer to avoid having a second one.

By the way, I didn't review properly the last three commits because I wanted to tackle my previous concern first.

lib/sgetgrent.c Show resolved Hide resolved
libmisc/malloc.c Outdated Show resolved Hide resolved
libmisc/malloc.c Outdated Show resolved Hide resolved
lib/subordinateio.c Outdated Show resolved Hide resolved
lib/malloc.h Outdated

#define REALLOCARRAY(ptr, n, type) \
({ \
__auto_type p_ = (ptr); \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is __auto_type standard C?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. It's GNU C, and available in GCC and Clang. ISO C23 will likely add auto with the same meaning, AFAIK (and hopefully deprecate the old meaning of auto).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, another GNU extension used here (also available at least in GCC and Clang) is ({ ... }): https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html.

This is a very basic feature for writing safe macros, and any good compiler should support it, but AFAIK it's not yet in the standard.

libmisc/env.c Show resolved Hide resolved
@alejandro-colomar
Copy link
Collaborator Author

Changes:

  • Rebased to master
$ git range-diff gh/alloc...alloc
 -:  -------- >  1:  ca9e309d Fix VPATH build
 -:  -------- >  2:  a187ad8e agetpass.c: Use SPDX tags
 -:  -------- >  3:  7e213cfb Add stpeprintf()
 -:  -------- >  4:  46610792 Use stpeprintf() where appropriate
 -:  -------- >  5:  8a9285aa Remove unnecessary NUL terminators
 -:  -------- >  6:  e0e9e57a Add mempcpy(3)
 -:  -------- >  7:  709e6b44 Add stpecpy()
 -:  -------- >  8:  5956cea1 Use stpecpy() where appropriate
 1:  03aa642e =  9:  231aa1cc Rely on realloc(NULL, ...) being equivalent to malloc(...)
 2:  b9355fd6 = 10:  820e65a4 malloc(3) already sets errno to ENOMEM
 3:  fd163a61 = 11:  27681f5e Use reallocf(3) instead of its pattern
 4:  8fa33624 = 12:  1ca3a789 Use reallocarray(3) instead of its pattern
 5:  60e1bee6 = 13:  56e751b2 Use calloc(3) instead of its pattern
 6:  ebc30525 ! 14:  7c4f8c73 Add safer allocation functions
    @@ libmisc/Makefile.am: libmisc_la_SOURCES = \
        loginprompt.c \
        mail.c \
     +  malloc.c \
    +   mempcpy.c \
        motd.c \
        myname.c \
    -   obscure.c \
     @@ libmisc/Makefile.am: libmisc_la_SOURCES = \
        xgetgrnam.c \
        xgetgrgid.c \
 7:  31471ecb = 15:  82dd2db7 Use xcalloc(3) instead of its pattern
 8:  918a3957 = 16:  52c661cd Use the new header for xstrdup()
 9:  1676ebad = 17:  d9da1bd0 Use *array() allocation functions where appropriate
10:  f87ad6ef = 18:  6fec8ba7 Use reallocarrayf() instead of its pattern
11:  6066d8fd = 19:  15e1695c Use xreallocarray() instead of its pattern
12:  e7416270 = 20:  09518b7b Add safer allocation macros
13:  6eaf62e8 ! 21:  87aeb91d Use safer allocation macros
    @@ libmisc/idmapping.c
     +
     +#include "malloc.h"
      #include "prototypes.h"
    + #include "stpeprintf.h"
      #include "idmapping.h"
    - #if HAVE_SYS_CAPABILITY_H
     @@ libmisc/idmapping.c: struct map_range *get_map_ranges(int ranges, int argc, char **argv)
                return NULL;
        }
    @@ libmisc/idmapping.c: void write_mapping(int proc_dir_fd, int ranges, const struc
        bufsize = ranges * ((ULONG_DIGITS + 1) * 3);
     -  pos = buf = xmalloc(bufsize);
     +  pos = buf = XMALLOCARRAY(bufsize, char);
    +   end = buf + bufsize;
      
        /* Build the mapping command */
    -   mapping = mappings;
     
      ## libmisc/list.c ##
     @@
    @@ src/groupmod.c: static void prepare_failure_reports (void)
      #endif
        info_passwd.name  = group_name;
      
    --  info_group.audit_msg   = xmalloc (512);
    -+  info_group.audit_msg   = XMALLOCARRAY (512, char);
    +-  gr                     = xmalloc (512);
    ++  gr                     = XMALLOCARRAY(512, char);
    +   info_group.audit_msg   = gr;
    +   gr_end                 = gr + 512;
      #ifdef    SHADOWGRP
    --  info_gshadow.audit_msg = xmalloc (512);
    -+  info_gshadow.audit_msg = XMALLOCARRAY (512, char);
    +-  sgr                    = xmalloc (512);
    ++  sgr                    = XMALLOCARRAY(512, char);
    +   info_gshadow.audit_msg = sgr;
    +   sgr_end                = sgr + 512;
      #endif
    --  info_passwd.audit_msg  = xmalloc (512);
    -+  info_passwd.audit_msg  = XMALLOCARRAY (512, char);
    +-  pw                     = xmalloc (512);
    ++  pw                     = XMALLOCARRAY(512, char);
    +   info_passwd.audit_msg  = pw;
    +   pw_end                 = pw + 512;
      
    -   (void) snprintf (info_group.audit_msg, 512,
    -                    "changing %s; ", gr_dbname ());
     
      ## src/groups.c ##
     @@
14:  ea048a41 = 22:  c86a6b73 Fix use-after-free of pointer after realloc(3)

@alejandro-colomar
Copy link
Collaborator Author

Changes:

$ git range-diff c86a6b73...alloc 
1:  d9da1bd0 ! 1:  648fc0ad Use *array() allocation functions where appropriate
    @@ lib/subordinateio.c: static bool append_range(struct subid_range **ranges, const
        } else {
                struct subid_range *alloced;
     -          alloced = realloc(*ranges, (n + 1) * (sizeof(struct subid_range)));
    -+          alloced = reallocarray(*ranges, n+1, sizeof(struct subid_range));
    ++          alloced = reallocarray(*ranges, n + 1, sizeof(struct subid_range));
                if (!alloced)
                        return false;
                *ranges = alloced;
2:  6fec8ba7 = 2:  8710f813 Use reallocarrayf() instead of its pattern
3:  15e1695c = 3:  9fffea5d Use xreallocarray() instead of its pattern
4:  09518b7b = 4:  a6d04d28 Add safer allocation macros
5:  87aeb91d ! 5:  02ef2cc4 Use safer allocation macros
    @@ lib/subordinateio.c: static bool have_range(struct commonio_db *db,
                        return false;
        } else {
                struct subid_range *alloced;
    --          alloced = reallocarray(*ranges, n+1, sizeof(struct subid_range));
    +-          alloced = reallocarray(*ranges, n + 1, sizeof(struct subid_range));
     +          alloced = REALLOCARRAY(*ranges, n + 1, struct subid_range);
                if (!alloced)
                        return false;
6:  c86a6b73 = 6:  5d215a3a Fix use-after-free of pointer after realloc(3)

@alejandro-colomar
Copy link
Collaborator Author

alejandro-colomar commented Feb 16, 2023

Two major comments apart from the ones inline. The first one is related to the malloc.* files naming. As they contain more functionality apart from malloc I wonder if we should rename it to something more generic.

I thought about calling it alloc.c. Does it sound better to you?

The second one is related to the new macros provided in e741627. Wouldn't it be better to add this functionality to the functions themselves? We are already adding an abstraction and I'd prefer to avoid having a second one.

I put most of the functionality in the functions, as they are safer, and then only put in macros what can't be put in functions, which is the cast and sizeof() and typeof(). It's not possible to implement that as functions in C. Type-generic code can only go in macros (C++ would have templates for that, although they don't convince me very much).

By the way, I didn't review properly the last three commits because I wanted to tackle my previous concern first.

Sure, makes sense.

wangliu-iscas pushed a commit to plctlab/patchwork-gcc that referenced this pull request Feb 17, 2023
Link: <https://inbox.sourceware.org/gcc/3098fd18-9dbf-b4e9-bae5-62ec6fea74cd@opteya.com/T/>
Link: <shadow-maint/shadow#649 (comment)>
Cc: Andreas Schwab <schwab@linux-m68k.org>
Cc: David Malcolm <dmalcolm@redhat.com>
Cc: Florian Weimer <fweimer@redhat.com>
Cc: Iker Pedrosa <ipedrosa@redhat.com>
Cc: Jens Gustedt <jens.gustedt@inria.fr>
Cc: Jonathan Wakely <jwakely.gcc@gmail.com>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Martin Uecker <uecker@tugraz.at>
Cc: Michael Kerrisk <mtk.manpages@gmail.com>
Cc: Paul Eggert <eggert@cs.ucla.edu>
Cc: Sam James <sam@gentoo.org>
Cc: Siddhesh Poyarekar <siddhesh@gotplt.org>
Cc: Yann Droneaud <ydroneaud@opteya.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
@alejandro-colomar
Copy link
Collaborator Author

Changes:

  • Rebase to master

@alejandro-colomar
Copy link
Collaborator Author

Changes:

  • Move malloc.c to alloc.c (also, do it in a separate commit, to simplify patches)
  • Fix typo in intermediate commit (it was not a problem, since it had been fixed in another commit)
$ git range-diff 5956cea1..5d215a3a master..alloc 
 1:  231aa1cc =  1:  8b33eced Rely on realloc(NULL, ...) being equivalent to malloc(...)
 2:  820e65a4 =  2:  303882fb malloc(3) already sets errno to ENOMEM
 3:  27681f5e =  3:  b055d69c Use reallocf(3) instead of its pattern
 4:  1ca3a789 !  4:  1cc8ce7d Use reallocarray(3) instead of its pattern
    @@ libmisc/env.c: void addenv (const char *string, /*@null@*/const char *value)
     -          newsize = (newenvc + NEWENVP_STEP) * sizeof (char *);
     -          __newenvp = (char **) realloc (newenvp, newsize);
     +          __newenvp = (char **) reallocarray (newenvp, newenvc + NEWENVP_STEP,
    -+                                              sizeof (char *);
    ++                                              sizeof (char *));
      
                if (NULL != __newenvp) {
                        /*
 5:  56e751b2 =  5:  1e37217a Use calloc(3) instead of its pattern
 -:  -------- >  6:  3fedd07c libmisc: Move xmalloc.c to alloc.c
 6:  7c4f8c73 !  7:  fdfeb00b Add safer allocation functions
    @@ Metadata
     Author: Alejandro Colomar <alx@kernel.org>
     
      ## Commit message ##
    -    Add safer allocation functions
    +    libmisc: Add safer allocation functions
     
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
    - ## lib/malloc.h (new) ##
    + ## lib/alloc.h ##
     @@
     +/*
     + * SPDX-FileCopyrightText:  2023, Alejandro Colomar <alx@kernel.org>
    @@ lib/malloc.h (new)
     +
     +#include <assert.h>
     +#include <errno.h>
    -+#include <stddef.h>
    + #include <stddef.h>
     +#include <stdint.h>
     +#include <stdlib.h>
     +
    @@ lib/malloc.h (new)
     +  return strcpy(XMALLOCARRAY(strlen(str) + 1, char), str);
     +}
     +
    -+
    -+#endif  // include guard
    -
    - ## lib/prototypes.h ##
    -@@ lib/prototypes.h: extern int setutmp (struct utmp *ut);
    - /* valid.c */
    - extern bool valid (const char *, const struct passwd *);
      
     -/* xmalloc.c */
     -extern /*@maynotreturn@*/ /*@only@*//*@out@*//*@notnull@*/void *xmalloc (size_t size)
     -  /*@ensures MaxSet(result) == (size - 1); @*/;
     -extern /*@maynotreturn@*/ /*@only@*//*@notnull@*/char *xstrdup (const char *);
    --
    - /* xgetpwnam.c */
    - extern /*@null@*/ /*@only@*/struct passwd *xgetpwnam (const char *);
    - /* xgetpwuid.c */
    -
    - ## libmisc/Makefile.am ##
    -@@ libmisc/Makefile.am: libmisc_la_SOURCES = \
    -   list.c log.c \
    -   loginprompt.c \
    -   mail.c \
    -+  malloc.c \
    -   mempcpy.c \
    -   motd.c \
    -   myname.c \
    -@@ libmisc/Makefile.am: libmisc_la_SOURCES = \
    -   xgetgrnam.c \
    -   xgetgrgid.c \
    -   xgetspnam.c \
    --  xmalloc.c \
    -   yesno.c
    - 
    - if WITH_BTRFS
    ++#endif  // include guard
     
    - ## libmisc/xmalloc.c => libmisc/malloc.c ##
    + ## libmisc/alloc.c ##
     @@
       * SPDX-FileCopyrightText: 1996 - 1998, Marek Michałkiewicz
       * SPDX-FileCopyrightText: 2003 - 2006, Tomasz Kłoczko
    @@ libmisc/xmalloc.c => libmisc/malloc.c
      #ident "$Id$"
      
     -#include <stdio.h>
    -+#include "malloc.h"
    ++#include "alloc.h"
     +
      #include <errno.h>
     +#include <stddef.h>
 7:  82dd2db7 =  8:  9ecfbc12 Use xcalloc(3) instead of its pattern
 8:  52c661cd <  -:  -------- Use the new header for xstrdup()
 9:  648fc0ad =  9:  9e64c102 Use *array() allocation functions where appropriate
10:  8710f813 = 10:  f7114806 Use reallocarrayf() instead of its pattern
11:  9fffea5d = 11:  a5cbc5f5 Use xreallocarray() instead of its pattern
12:  a6d04d28 ! 12:  775f0978 Add safer allocation macros
    @@ Metadata
     Author: Alejandro Colomar <alx@kernel.org>
     
      ## Commit message ##
    -    Add safer allocation macros
    +    libmisc: Add safer allocation macros
     
         This macros have several benefits over the standard functions:
     
    @@ Commit message
         Cc: Valentin V. Bartenev <vbartenev@gmail.com>
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
    - ## lib/malloc.h ##
    + ## lib/alloc.h ##
     @@
      #include "defines.h"
      
13:  02ef2cc4 ! 13:  d15ee245 Use safer allocation macros
    @@ lib/commonio.c
      #include <stdio.h>
      #include <signal.h>
     +
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "nscd.h"
      #include "sssd.h"
      #ifdef WITH_TCB
    @@ lib/getdef.c
      #include <libeconf.h>
      #endif
     +
    ++#include "alloc.h"
      #include "getdef.h"
    -+#include "malloc.h"
      #include "shadowlog_internal.h"
     +
      /*
    @@ lib/groupio.c
      #include <assert.h>
      #include <stdio.h>
      
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "prototypes.h"
      #include "defines.h"
      #include "commonio.h"
    @@ lib/groupmem.c
      
      #ident "$Id$"
      
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "prototypes.h"
      #include "defines.h"
      #include "groupio.h"
    @@ lib/gshadow.c
      #include <stdio.h>
      #include <string.h>
     +
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "prototypes.h"
      #include "defines.h"
     +
    @@ lib/nss.c
      #include <ctype.h>
      #include <stdatomic.h>
     +
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "prototypes.h"
      #include "../libsubid/subid.h"
      #include "shadowlog_internal.h"
    @@ lib/pwmem.c
      
      #include <stdio.h>
     +
    ++#include "alloc.h"
      #include "defines.h"
    -+#include "malloc.h"
      #include "prototypes.h"
      #include "pwio.h"
    - 
     @@
      {
        struct passwd *pw;
    @@ lib/run_part.c
      #include <unistd.h>
      #include <lib/prototypes.h>
     +
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "run_part.h"
      #include "shadowlog_internal.h"
      
    @@ lib/sgetgrent.c
      #include <sys/types.h>
      #include <grp.h>
     +
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "defines.h"
      #include "prototypes.h"
      
    @@ lib/sgroupio.c
      
      #ident "$Id$"
      
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "prototypes.h"
      #include "defines.h"
      #include "commonio.h"
    @@ lib/shadowmem.c
      #include <shadow.h>
      #include <stdio.h>
     +
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "shadowio.h"
      
      /*@null@*/ /*@only@*/struct spwd *__spw_dup (const struct spwd *spent)
    @@ lib/sssd.c
      #include <sys/wait.h>
      #include <sys/types.h>
     +
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "exitcodes.h"
      #include "defines.h"
      #include "prototypes.h"
    @@ lib/subordinateio.c
      #include <ctype.h>
      #include <fcntl.h>
      
    -+#include "malloc.h"
    ++#include "alloc.h"
     +
      #define ID_SIZE 31
      
    @@ libmisc/addgrps.c
      #include <grp.h>
      #include <errno.h>
     +
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "shadowlog.h"
      
      #ident "$Id$"
    @@ libmisc/agetpass.c
      
      #ident "$Id$"
      
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "prototypes.h"
      
      
    @@ libmisc/agetpass.c: agetpass(const char *prompt)
      
     
      ## libmisc/copydir.c ##
    -@@
    - #include <sys/time.h>
    - #include <fcntl.h>
    - #include <stdio.h>
    -+
    -+#include "malloc.h"
    - #include "prototypes.h"
    - #include "defines.h"
    - #ifdef WITH_SELINUX
     @@ libmisc/copydir.c: static /*@exposed@*/ /*@null@*/struct link_name *check_link (const char *name, c
                return NULL;
        }
    @@ libmisc/copydir.c: static int copy_symlink (const struct path_info *src, const s
                                 oldlink + strlen (src_orig));
     
      ## libmisc/env.c ##
    -@@
    - #include <stdio.h>
    - #include <stdlib.h>
    - #include <string.h>
    -+
    -+#include "malloc.h"
    - #include "prototypes.h"
    - #include "defines.h"
    - #include "shadowlog.h"
     @@ libmisc/env.c: static const char *const noslash[] = {
       */
      void initenv (void)
    @@ libmisc/env.c: void addenv (const char *string, /*@null@*/const char *value)
                 */
      
     -          __newenvp = (char **) reallocarray (newenvp, newenvc + NEWENVP_STEP,
    --                                              sizeof (char *);
    +-                                              sizeof (char *));
     +          __newenvp = REALLOCARRAY(newenvp, newenvc + NEWENVP_STEP, char *);
      
                if (NULL != __newenvp) {
    @@ libmisc/find_new_gid.c
      #include <stdio.h>
      #include <errno.h>
      
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "prototypes.h"
      #include "groupio.h"
      #include "getdef.h"
    @@ libmisc/find_new_uid.c
      #include <stdio.h>
      #include <errno.h>
      
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "prototypes.h"
      #include "pwio.h"
      #include "getdef.h"
    @@ libmisc/idmapping.c
      #include <stdlib.h>
      #include <stdio.h>
     +
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "prototypes.h"
      #include "stpeprintf.h"
      #include "idmapping.h"
    @@ libmisc/list.c
      
      #include <assert.h>
     +
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "prototypes.h"
      #include "defines.h"
      /*
    @@ libmisc/loginprompt.c
      #include <signal.h>
      #include <ctype.h>
     +
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "prototypes.h"
      #include "defines.h"
      #include "getdef.h"
    @@ libmisc/mail.c
      #include <stdio.h>
      #include <string.h>
      
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "getdef.h"
      
      #ident "$Id$"
    @@ libmisc/obscure.c
      #include <ctype.h>
      #include <stdio.h>
     +
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "prototypes.h"
      #include "defines.h"
      #include "getdef.h"
    @@ libmisc/pam_pass_non_interactive.c
      #include <stdlib.h>
      #include <security/pam_appl.h>
     +
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "prototypes.h"
      #include "shadowlog.h"
      
    @@ libmisc/prefix_flag.c
      #include <assert.h>
     +
      #include "defines.h"
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "prototypes.h"
      /*@-exitarg@*/
      #include "exitcodes.h"
    @@ libmisc/setupenv.c
      #include <stdio.h>
      #include <ctype.h>
     +
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "prototypes.h"
      #include "defines.h"
      #include <pwd.h>
    @@ libmisc/utmp.c
      #include <netdb.h>
      #include <stdio.h>
      
    -+#include "malloc.h"
    ++#include "alloc.h"
     +
      #ident "$Id$"
      
    @@ libmisc/xgetXXbyYY.c
      #include <stdio.h>
      #include <errno.h>
     +
    -+#include "malloc.h"
    ++#include "alloc.h"
      #include "prototypes.h"
      #include "shadowlog.h"
      
    @@ src/gpasswd.c
      #include <stdio.h>
      #include <sys/types.h>
     +
    ++#include "alloc.h"
      #include "defines.h"
      #include "groupio.h"
    -+#include "malloc.h"
      #include "nscd.h"
    - #include "sssd.h"
    - #include "prototypes.h"
     @@ src/gpasswd.c: static void get_group (struct group *gr)
      
                        sg->sg_mem = dup_list (gr->gr_mem);
    @@ src/gpasswd.c: static void get_group (struct group *gr)
     
      ## src/groupmems.c ##
     @@
    + #include "pam_defs.h"
      #endif                            /* USE_PAM */
      #include <pwd.h>
    ++
    ++#include "alloc.h"
      #include "defines.h"
    -+#include "malloc.h"
      #include "prototypes.h"
      #include "groupio.h"
    - #ifdef SHADOWGRP
     @@ src/groupmems.c: static void add_user (const char *user,
                        static struct sgrp sgrent;
                        sgrent.sg_name = xstrdup (newgrp->gr_name);
    @@ src/groupmems.c: static void purge_members (const struct group *grp)
     
      ## src/groupmod.c ##
     @@
    + #include <pwd.h>
    + #endif                            /* USE_PAM */
    + #endif                            /* ACCT_TOOLS_SETUID */
    ++
    ++#include "alloc.h"
      #include "chkname.h"
      #include "defines.h"
      #include "groupio.h"
    -+#include "malloc.h"
    - #include "pwio.h"
    - #include "nscd.h"
    - #include "sssd.h"
     @@ src/groupmod.c: static void grp_update (void)
                        // requested to replace the existing groups
                        if (NULL != grp.gr_mem[0])
    @@ src/groups.c
      #include <pwd.h>
      #include <stdio.h>
     +
    ++#include "alloc.h"
      #include "defines.h"
    -+#include "malloc.h"
      #include "prototypes.h"
      #include "shadowlog.h"
     +
    @@ src/id.c
      #include <stdio.h>
      #include <sys/types.h>
     +
    ++#include "alloc.h"
      #include "defines.h"
    -+#include "malloc.h"
     +
      /* local function prototypes */
      static void usage (void);
    @@ src/login.c
      #include <sys/ioctl.h>
      #include <assert.h>
     +
    ++#include "alloc.h"
      #include "defines.h"
      #include "faillog.h"
      #include "failure.h"
    - #include "getdef.h"
    -+#include "malloc.h"
    - #include "prototypes.h"
    - #include "pwauth.h"
    - /*@-exitarg@*/
     @@ src/login.c: int main (int argc, char **argv)
      #ifdef RLOGIN
        if (rflg) {
    @@ src/newgrp.c
      #include <stdio.h>
      #include <assert.h>
     +
    ++#include "alloc.h"
      #include "defines.h"
      #include "getdef.h"
    -+#include "malloc.h"
      #include "prototypes.h"
    - /*@-exitarg@*/
    - #include "exitcodes.h"
     @@ src/newgrp.c: int main (int argc, char **argv)
        /* don't use getgroups(0, 0) - it doesn't work on some systems */
        i = 16;
    @@ src/newgrp.c: int main (int argc, char **argv)
     
      ## src/newusers.c ##
     @@
    - #include "defines.h"
    - #include "getdef.h"
    - #include "groupio.h"
    -+#include "malloc.h"
    - #include "nscd.h"
    - #include "sssd.h"
    - #include "pwio.h"
    + #include <ctype.h>
    + #include <errno.h>
    + #include <string.h>
    ++
    ++#include "alloc.h"
    + #ifdef ACCT_TOOLS_SETUID
    + #ifdef USE_PAM
    + #include "pam_defs.h"
     @@ src/newusers.c: int main (int argc, char **argv)
      #ifdef USE_PAM
                /* keep the list of user/password for later update by PAM */
    @@ src/passwd.c
      #include <sys/types.h>
      #include <time.h>
     +
    ++#include "alloc.h"
      #include "defines.h"
      #include "getdef.h"
    -+#include "malloc.h"
      #include "nscd.h"
    - #include "sssd.h"
    - #include "prototypes.h"
     @@ src/passwd.c: static char *update_crypt_pw (char *cp)
        }
      
    @@ src/su.c
      #include <fcntl.h>
      #endif                            /* !USE_PAM */
     +
    ++#include "alloc.h"
      #include "prototypes.h"
      #include "defines.h"
    -+#include "malloc.h"
      #include "pwauth.h"
    - #include "getdef.h"
    - #ifdef USE_PAM
     @@ src/su.c: static void execve_shell (const char *shellname,
                while (NULL != args[n_args]) {
                        n_args++;
    @@ src/useradd.c
      #include <time.h>
      #include <unistd.h>
     +
    ++#include "alloc.h"
      #include "chkname.h"
      #include "defines.h"
      #include "faillog.h"
    - #include "getdef.h"
    - #include "groupio.h"
    -+#include "malloc.h"
    - #include "nscd.h"
    - #include "sssd.h"
    - #include "prototypes.h"
     @@ src/useradd.c: static void get_defaults (void)
                int wlen;
      
    @@ src/useradd.c: int main (int argc, char **argv)
     
      ## src/userdel.c ##
     @@
    - #include "defines.h"
    - #include "getdef.h"
    - #include "groupio.h"
    -+#include "malloc.h"
    - #include "nscd.h"
    - #include "sssd.h"
    - #include "prototypes.h"
    + #include <sys/stat.h>
    + #include <sys/types.h>
    + #include <unistd.h>
    ++
    ++#include "alloc.h"
    + #ifdef ACCT_TOOLS_SETUID
    + #ifdef USE_PAM
    + #include "pam_defs.h"
     @@ src/userdel.c: static int remove_mailbox (void)
        }
      
    @@ src/usermod.c
      #include <sys/types.h>
      #include <time.h>
     +
    ++#include "alloc.h"
      #include "chkname.h"
      #include "defines.h"
      #include "faillog.h"
    - #include "getdef.h"
    - #include "groupio.h"
    -+#include "malloc.h"
    - #include "nscd.h"
    - #include "sssd.h"
    - #include "prototypes.h"
     @@ src/usermod.c: static int prepend_range(const char *str, struct ulong_range_list_entry **head)
        if (range.first > range.last)
                return 0;
    @@ src/vipw.c
      #include <unistd.h>
      #include <utime.h>
     +
    ++#include "alloc.h"
      #include "defines.h"
      #include "groupio.h"
    -+#include "malloc.h"
      #include "nscd.h"
    - #include "sssd.h"
    - #include "prototypes.h"
     @@ src/vipw.c: vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void))
                                continue;
                }
14:  5d215a3a = 14:  18655c0d Fix use-after-free of pointer after realloc(3)

inline char *
xstrdup(const char *str)
{
return strcpy(XMALLOCARRAY(strlen(str) + 1, char), str);

Check failure

Code scanning / CodeQL

Unbounded write

This 'call to strcpy' with input from [call to getenv](1) may overflow the destination. This 'call to strcpy' with input from [tzbuf](2) may overflow the destination. This 'call to strcpy' with input from [argv](3) may overflow the destination. This 'call to strcpy' with input from [argv](4) may overflow the destination. This 'call to strcpy' with input from [argv](5) may overflow the destination. This 'call to strcpy' with input from [argv](6) may overflow the destination. This 'call to strcpy' with input from [argv](7) may overflow the destination. This 'call to strcpy' with input from [buf](8) may overflow the destination.
lib/alloc.h Outdated Show resolved Hide resolved
@ikerexxe
Copy link
Collaborator

I thought about calling it alloc.c. Does it sound better to you?

Yes 😄

I put most of the functionality in the functions, as they are safer, and then only put in macros what can't be put in functions, which is the cast and sizeof() and typeof(). It's not possible to implement that as functions in C. Type-generic code can only go in macros (C++ would have templates for that, although they don't convince me very much).

C++ templates are so handy...

By the way, I didn't review properly the last three commits because I wanted to tackle my previous concern first.

Reviewed.

This is guaranteed by ISO C.  Now that we require ISO C (and even POSIX)
to compile, we can simplify this code.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
lib/sgetgrent.c Outdated Show resolved Hide resolved
@hallyn
Copy link
Member

hallyn commented Feb 22, 2023 via email

@alejandro-colomar
Copy link
Collaborator Author

Huh. I don't know where I was looking before, but now I see:
SUSv2 requires malloc(), calloc(), and realloc() to set errno to ENOMEM upon failure.
( https://manpages.ubuntu.com/manpages/jammy/en/man3/malloc.3.html ) So all's good, thanks.

Thank you!

BTW, that text is a bit old for me :D It was largely rewritten in

commit cfc381be29fb7b4f642b8b333882f22628112abe
Author: Paul Eggert <eggert@cs.ucla.edu>
Date:   Tue Aug 10 12:37:07 2021 -0700

    malloc.3: Modernize for glibc 2.34
    
    glibc has tightened up its rules for replacing the memory
    allocator.  I went through the malloc man page and looked for how
    it documented malloc() and related functions, and fixed
    discrepancies with glibc malloc() documentation and/or
    implementation.  I also reorganized the portability discussion so
    that portability issues can be seen more clearly.
    
    Signed-off-by: Michael Kerrisk <mtk.manpages@gmail.com>

libmisc/addgrps.c Outdated Show resolved Hide resolved
@hallyn
Copy link
Member

hallyn commented Feb 23, 2023

Let's see what @hallyn thinks about it :)

Mostly looks good to me, thanks - I still need to look through 12-14, but have to run now.

lib/alloc.h Show resolved Hide resolved
#define REALLOC(ptr, type) REALLOCARRAY(ptr, 1, type)
#define REALLOCF(ptr, type) REALLOCARRAYF(ptr, 1, type)

#define REALLOCARRAY(ptr, n, type) \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once these are merged, it might be worth having a CI step that looks for non-modified users so we can catch them before they go in.

lib/alloc.h Outdated Show resolved Hide resolved
@hallyn hallyn self-requested a review February 23, 2023 20:01
@alejandro-colomar
Copy link
Collaborator Author

Changes:

  • Simplify ALLOCARRAY
$ git range-diff master..gh/alloc master..alloc 
 1:  cbf47ca9 =  1:  cbf47ca9 Rely on realloc(NULL, ...) being equivalent to malloc(...)
 2:  a921c9db =  2:  a921c9db malloc(3) already sets errno to ENOMEM
 3:  4cc9c43c =  3:  4cc9c43c Use reallocf(3) instead of its pattern
 4:  27768bd7 =  4:  27768bd7 Use reallocarray(3) instead of its pattern
 5:  2d558082 =  5:  2d558082 Use calloc(3) instead of its pattern
 6:  bac52609 =  6:  bac52609 libmisc: Move xmalloc.c to alloc.c
 7:  fb2bbb75 =  7:  fb2bbb75 libmisc: Add safer allocation functions
 8:  16cbd0f1 =  8:  16cbd0f1 Use xcalloc(3) instead of its pattern
 9:  bdd5c8d1 =  9:  bdd5c8d1 Use *array() allocation functions where appropriate
10:  9e00e7b5 = 10:  9e00e7b5 Use reallocarrayf() instead of its pattern
11:  b6f9bb55 = 11:  b6f9bb55 Use xreallocarray() instead of its pattern
12:  9d6f4149 ! 12:  a7ac2a00 libmisc: Add safer allocation macros
    @@ lib/alloc.h
      #include "defines.h"
      
      
    ++#define ALLOCARRAY(n, type)    ((type *) alloca(sizeof(type) * (n)))
     +#define CALLOC(n, type)        ((type *) calloc(n, sizeof(type)))
     +#define XCALLOC(n, type)       ((type *) xcalloc(n, sizeof(type)))
     +#define MALLOCARRAY(n, type)   ((type *) mallocarray(n, sizeof(type)))
    @@ lib/alloc.h
     +  (type *) xreallocarray(p_, n, sizeof(type));                          \
     +})
     +
    -+#define ALLOCARRAY(n, type)                                                   \
    -+({                                                                            \
    -+  type    *p_;                                                          \
    -+  size_t  n_ = (n);                                                     \
    -+                                                                              \
    -+  if (n_ != 0 && n_ > SSIZE_MAX / n_)                                   \
    -+          p_ = NULL;                                                    \
    -+  else                                                                  \
    -+          p_ = alloca(sizeof(type) * n_);                               \
    -+                                                                              \
    -+  p_;                                                                   \
    -+})
    -+
     +
      ATTR_MALLOC(free)
      inline void *xmalloc(size_t size);
13:  a383e44e = 13:  5282a360 Use safer allocation macros
14:  7339875c = 14:  edf3a6ab Fix use-after-free of pointer after realloc(3)

In addition, don't set local variables just before return.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
We'll expand the contents in a following commit, so let's move the file
to a more generic name, have a dedicated header, and update includes.

Signed-off-by: Alejandro Colomar <alx@kernel.org>

Use the new header for xstrdup()

Signed-off-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
This prevents overflow from multiplication.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
This macros have several benefits over the standard functions:

-  The type of the allocated object (not the pointer) is specified as an
   argument, which improves readability:
   -  It is directly obvious what is the type of the object just by
      reading the macro call.
   -  It allows grepping for all allocations of a given type.

   This is admittedly similar to using sizeof() to get the size of the
   object, but we'll see why this is better.

-  In the case of reallocation macros, an extra check is performed to
   make sure that the previous pointer was compatible with the allocated
   type, which can avoid some mistakes.

-  The cast is performed automatically, with a pointer type derived from
   the type of the object.  This is the best point of this macro, since
   it does an automatic cast, where there's no chance of typos.

   Usually, programmers have to decide whether to cast or not the result
   of malloc(3).  Casts usually hide warnings, so are to be avoided.
   However, these functions already return a void *, so a cast doesn't
   really add much danger.  Moreover, a cast can even add warnings in
   this exceptional case, if the type of the cast is different than the
   type of the assigned pointer.  Performing a manual cast is still not
   perfect, since there are chances that a mistake will be done, and
   even ignoring accidents, they clutter code, hurting readability.
   And now we have a cast that is synced with sizeof.

-  Whenever the type of the object changes, since we perform an explicit
   cast to the old type, there will be a warning due to type mismatch in
   the assignment, so we'll be able to see all lines that are affected
   by such a change.  This is especially important, since changing the
   type of a variable and missing to update an allocation call far away
   from the declaration is easy, and the consequences can be quite bad.

Cc: Valentin V. Bartenev <vbartenev@gmail.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Use of these macros, apart from the benefits mentioned in the commit
that adds the macros, has some other good side effects:

-  Consistency in getting the size of the object from sizeof(type),
   instead of a mix of sizeof(type) sometimes and sizeof(*p) other
   times.

-  More readable code: no casts, and no sizeof(), so also shorter lines
   that we don't need to cut.

-  Consistency in using array allocation calls for allocations of arrays
   of objects, even when the object size is 1.

Cc: Valentin V. Bartenev <vbartenev@gmail.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
We can't use a pointer that was input to realloc(3), nor any pointers
that point to reallocated memory, without making sure that the memory
wasn't moved.  If we do, the Behavior is Undefined.

Signed-off-by: Alejandro Colomar <alx@kernel.org>
@alejandro-colomar
Copy link
Collaborator Author

Changes:

  • Fix commit message
$ git range-diff master..gh/alloc master..alloc 
 1:  cbf47ca9 =  1:  cbf47ca9 Rely on realloc(NULL, ...) being equivalent to malloc(...)
 2:  a921c9db =  2:  a921c9db malloc(3) already sets errno to ENOMEM
 3:  4cc9c43c !  3:  eac5a5ff Use reallocf(3) instead of its pattern
    @@ Metadata
      ## Commit message ##
         Use reallocf(3) instead of its pattern
     
    -    In addition, don't set local variables just after return.
    +    In addition, don't set local variables just before return.
     
         Signed-off-by: Alejandro Colomar <alx@kernel.org>
     
 4:  27768bd7 =  4:  42f13e8d Use reallocarray(3) instead of its pattern
 5:  2d558082 =  5:  18b3d83a Use calloc(3) instead of its pattern
 6:  bac52609 =  6:  c431a72c libmisc: Move xmalloc.c to alloc.c
 7:  fb2bbb75 =  7:  63f959f7 libmisc: Add safer allocation functions
 8:  16cbd0f1 =  8:  a85b0bda Use xcalloc(3) instead of its pattern
 9:  bdd5c8d1 =  9:  0bb64e37 Use *array() allocation functions where appropriate
10:  9e00e7b5 = 10:  8f706fa9 Use reallocarrayf() instead of its pattern
11:  b6f9bb55 = 11:  061a04d5 Use xreallocarray() instead of its pattern
12:  a7ac2a00 = 12:  186ca04e libmisc: Add safer allocation macros
13:  5282a360 = 13:  9646f692 Use safer allocation macros
14:  edf3a6ab = 14:  11d92302 Fix use-after-free of pointer after realloc(3)

@hallyn hallyn merged commit 7668f77 into shadow-maint:master Feb 24, 2023
@@ -76,7 +76,7 @@ static uid_t getnamuid(const char *name) {
}

static int alloc_uid(uid_t **uids, uid_t id) {
*uids = malloc(sizeof(uid_t));
*uids = MALLOC(uid_t);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, this is actually breaking build of libsubid_zzz during tests.

Not sure yet whether just adding #include "alloc.h" at the top suffices.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that should work.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if that library links to libmisc, though, which might be a problem if not.

skissane pushed a commit to skissane/man-pages that referenced this pull request Feb 26, 2023
mallocarray() is safer than malloc(3), since it checks for overflow; it
should be preferred almost always (with the exception of non-arrays
maybe).

The macros like MALLOCARRAY() --and MALLOC()-- that perform automatic
casting and sizeof() are also safer than calling the functions directly:

-  The type of the allocated object (not the pointer) is specified as an
   argument, which improves readability:
   -  It is directly obvious what is the type of the object just by
      reading the macro call.
   -  It allows grepping for all allocations of a given type.

   This is admittedly similar to using sizeof() to get the size of the
   object, but we'll see why this is better.

-  In the case of reallocation macros, an extra check is performed to
   make sure that the previous pointer was compatible with the allocated
   type, which can avoid some mistakes.

-  The cast is performed automatically, with a pointer type derived from
   the type of the object.  This is the best point of this macro, since
   it does an automatic cast, where there's no chance of typos.

   Usually, programmers have to decide whether to cast or not the result
   of malloc(3).  Casts usually hide warnings, so are to be avoided.
   However, these functions already return a void *, so a cast doesn't
   really add much danger.  Moreover, a cast can even add warnings in
   this exceptional case, if the type of the cast is different than the
   type of the assigned pointer.  Performing a manual cast is still not
   perfect, since there are chances that a mistake will be done, and
   even ignoring accidents, they clutter code, hurting readability.
   And now we have a cast that is synced with sizeof.

-  Whenever the type of the object changes, since we perform an explicit
   cast to the old type, there will be a warning due to type mismatch in
   the assignment, so we'll be able to see all lines that are affected
   by such a change.  This is especially important, since changing the
   type of a variable and missing to update an allocation call far away
   from the declaration is easy, and the consequences can be quite bad

Apart from those benefits, there are other minor style benefits:

-  Consistency in getting the size of the object from sizeof(type),
   instead of a mix of sizeof(type) sometimes and sizeof(*p) other
   times.

-  More readable code: no casts, and no sizeof(), so also shorter lines
   that we don't need to cut.

-  Consistency in using array allocation calls for allocations of arrays
   of objects, even when the object size is 1.

Link: <shadow-maint/shadow#649>
Cc: Serge Hallyn <serge@hallyn.com>
Cc: Iker Pedrosa <ipedrosa@redhat.com>
Cc: "Valentin V. Bartenev" <vbartenev@gmail.com>
Signed-off-by: Alejandro Colomar <alx@kernel.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants