On Fri, Oct 25, 2024 at 02:08:08PM +0200, Stefano Brivio wrote: > clang-tidy, starting from LLVM version 16, up to at least LLVM version > 19, now checks that we detect and handle errors for snprintf() as > requested by CERT C rule ERR33-C. These warnings were logged with LLVM > version 19.1.2 (at least Debian and Fedora match): > > /home/sbrivio/passt/arch.c:43:3: error: the value returned by this function should not be disregarded; neglecting it may lead to errors [cert-err33-c,-warnings-as-errors] > 43 | snprintf(new_path, PATH_MAX + sizeof(".avx2"), "%s.avx2", exe); > | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > /home/sbrivio/passt/arch.c:43:3: note: cast the expression to void to silence this warning > /home/sbrivio/passt/conf.c:577:4: error: the value returned by this function should not be disregarded; neglecting it may lead to errors [cert-err33-c,-warnings-as-errors] > 577 | snprintf(netns, PATH_MAX, "/proc/%ld/ns/net", pidval); > | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > /home/sbrivio/passt/conf.c:577:4: note: cast the expression to void to silence this warning > /home/sbrivio/passt/conf.c:579:5: error: the value returned by this function should not be disregarded; neglecting it may lead to errors [cert-err33-c,-warnings-as-errors] > 579 | snprintf(userns, PATH_MAX, "/proc/%ld/ns/user", > | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > 580 | pidval); > | ~~~~~~~ > /home/sbrivio/passt/conf.c:579:5: note: cast the expression to void to silence this warning > /home/sbrivio/passt/pasta.c:105:2: error: the value returned by this function should not be disregarded; neglecting it may lead to errors [cert-err33-c,-warnings-as-errors] > 105 | snprintf(ns, PATH_MAX, "/proc/%i/ns/net", pasta_child_pid); > | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > /home/sbrivio/passt/pasta.c:105:2: note: cast the expression to void to silence this warning > /home/sbrivio/passt/pasta.c:242:2: error: the value returned by this function should not be disregarded; neglecting it may lead to errors [cert-err33-c,-warnings-as-errors] > 242 | snprintf(uidmap, BUFSIZ, "0 %u 1", uid); > | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > /home/sbrivio/passt/pasta.c:242:2: note: cast the expression to void to silence this warning > /home/sbrivio/passt/pasta.c:243:2: error: the value returned by this function should not be disregarded; neglecting it may lead to errors [cert-err33-c,-warnings-as-errors] > 243 | snprintf(gidmap, BUFSIZ, "0 %u 1", gid); > | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > /home/sbrivio/passt/pasta.c:243:2: note: cast the expression to void to silence this warning > /home/sbrivio/passt/tap.c:1155:4: error: the value returned by this function should not be disregarded; neglecting it may lead to errors [cert-err33-c,-warnings-as-errors] > 1155 | snprintf(path, UNIX_PATH_MAX - 1, UNIX_SOCK_PATH, i); > | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > /home/sbrivio/passt/tap.c:1155:4: note: cast the expression to void to silence this warning > > Don't silence the warnings as they might actually have some merit. Add > an snprintf_check() function, instead, checking that we're not > truncating messages while printing to buffers, and terminate if the > check fails. > > Signed-off-by: Stefano Brivio Reviewed-by: David Gibson > --- > arch.c | 6 +++++- > conf.c | 13 +++++++++---- > pasta.c | 11 ++++++++--- > tap.c | 5 +++-- > util.c | 30 ++++++++++++++++++++++++++++++ > util.h | 2 ++ > 6 files changed, 57 insertions(+), 10 deletions(-) > > diff --git a/arch.c b/arch.c > index 04bebfc..d1dfb73 100644 > --- a/arch.c > +++ b/arch.c > @@ -19,6 +19,7 @@ > #include > > #include "log.h" > +#include "util.h" > > /** > * arch_avx2_exec() - Switch to AVX2 build if supported > @@ -40,7 +41,10 @@ void arch_avx2_exec(char **argv) > if (__builtin_cpu_supports("avx2")) { > char new_path[PATH_MAX + sizeof(".avx2")]; > > - snprintf(new_path, PATH_MAX + sizeof(".avx2"), "%s.avx2", exe); > + if (snprintf_check(new_path, PATH_MAX + sizeof(".avx2"), > + "%s.avx2", exe)) > + die_perror("Can't build AVX2 executable path"); > + > execve(new_path, argv, environ); > warn_perror("Can't run AVX2 build, using non-AVX2 version"); > } > diff --git a/conf.c b/conf.c > index b3b5342..fa5cec3 100644 > --- a/conf.c > +++ b/conf.c > @@ -574,10 +574,15 @@ static void conf_pasta_ns(int *netns_only, char *userns, char *netns, > if (pidval < 0 || pidval > INT_MAX) > die("Invalid PID %s", argv[optind]); > > - snprintf(netns, PATH_MAX, "/proc/%ld/ns/net", pidval); > - if (!*userns) > - snprintf(userns, PATH_MAX, "/proc/%ld/ns/user", > - pidval); > + if (snprintf_check(netns, PATH_MAX, > + "/proc/%ld/ns/net", pidval)) > + die_perror("Can't build netns path"); > + > + if (!*userns) { > + if (snprintf_check(userns, PATH_MAX, > + "/proc/%ld/ns/user", pidval)) > + die_perror("Can't build userns path"); > + } > } > } > > diff --git a/pasta.c b/pasta.c > index 307fb4a..a117704 100644 > --- a/pasta.c > +++ b/pasta.c > @@ -102,7 +102,9 @@ static int pasta_wait_for_ns(void *arg) > int flags = O_RDONLY | O_CLOEXEC; > char ns[PATH_MAX]; > > - snprintf(ns, PATH_MAX, "/proc/%i/ns/net", pasta_child_pid); > + if (snprintf_check(ns, PATH_MAX, "/proc/%i/ns/net", pasta_child_pid)) > + die_perror("Can't build netns path"); > + > do { > while ((c->pasta_netns_fd = open(ns, flags)) < 0) { > if (errno != ENOENT) > @@ -239,8 +241,11 @@ void pasta_start_ns(struct ctx *c, uid_t uid, gid_t gid, > c->quiet = 1; > > /* Configure user and group mappings */ > - snprintf(uidmap, BUFSIZ, "0 %u 1", uid); > - snprintf(gidmap, BUFSIZ, "0 %u 1", gid); > + if (snprintf_check(uidmap, BUFSIZ, "0 %u 1", uid)) > + die_perror("Can't build uidmap"); > + > + if (snprintf_check(gidmap, BUFSIZ, "0 %u 1", gid)) > + die_perror("Can't build gidmap"); > > if (write_file("/proc/self/uid_map", uidmap) || > write_file("/proc/self/setgroups", "deny") || > diff --git a/tap.c b/tap.c > index c53a39b..cfb82e9 100644 > --- a/tap.c > +++ b/tap.c > @@ -1151,8 +1151,9 @@ int tap_sock_unix_open(char *sock_path) > > if (*sock_path) > memcpy(path, sock_path, UNIX_PATH_MAX); > - else > - snprintf(path, UNIX_PATH_MAX - 1, UNIX_SOCK_PATH, i); > + else if (snprintf_check(path, UNIX_PATH_MAX - 1, > + UNIX_SOCK_PATH, i)) > + die_perror("Can't build UNIX domain socket path"); > > ex = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); > if (ex < 0) > diff --git a/util.c b/util.c > index eba7d52..9cb705e 100644 > --- a/util.c > +++ b/util.c > @@ -749,3 +749,33 @@ void close_open_files(int argc, char **argv) > if (rc) > die_perror("Failed to close files leaked by parent"); > } > + > +/** > + * snprintf_check() - snprintf() wrapper, checking for truncation and errors > + * @str: Output buffer > + * @size: Maximum size to write to @str > + * @format: Message > + * > + * Return: false on success, true on truncation or error, sets errno on failure > + */ > +bool snprintf_check(char *str, size_t size, const char *format, ...) > +{ > + va_list ap; > + int rc; > + > + va_start(ap, format); > + rc = snprintf(str, size, format, ap); > + va_end(ap); > + > + if (rc < 0) { > + errno = EIO; > + return true; > + } > + > + if ((size_t)rc >= size) { > + errno = ENOBUFS; > + return true; > + } > + > + return false; > +} > diff --git a/util.h b/util.h > index 2c1e08e..96f178c 100644 > --- a/util.h > +++ b/util.h > @@ -11,6 +11,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -200,6 +201,7 @@ int write_file(const char *path, const char *buf); > int write_all_buf(int fd, const void *buf, size_t len); > int write_remainder(int fd, const struct iovec *iov, size_t iovcnt, size_t skip); > void close_open_files(int argc, char **argv); > +bool snprintf_check(char *str, size_t size, const char *format, ...); > > /** > * af_name() - Return name of an address family -- David Gibson (he or they) | I'll have my music baroque, and my code david AT gibson.dropbear.id.au | minimalist, thank you, not the other way | around. http://www.ozlabs.org/~dgibson