[PATCH v2 0/8] Take care of clang-tidy warnings with LLVM >= 16
So I started hitting some clang-tidy warnings with LLVM 16, some looked bogus, so I upgraded to LLVM 19, and... I got even more. This series takes care of them in different ways. v2: - make snprintf_check() return and set errno on failure, in 2/8 - add missing err_perror() calls on clock_gettime() failures in 6/8 - drop all explicit integer assignments in enum udp_iov_idx in 7/8 Stefano Brivio (8): Makefile: Exclude qrap.c from clang-tidy checks treewide: Comply with CERT C rule ERR33-C for snprintf() treewide: Silence cert-err33-c clang-tidy warnings for fprintf() Makefile: Disable readability-math-missing-parentheses clang-tidy check treewide: Suppress clang-tidy warning if we already use O_CLOEXEC or if we can't treewide: Address cert-err33-c clang-tidy warnings for clock and timer functions udp: Take care of cert-int09-c clang-tidy warning for enum udp_iov_idx util: Don't use errno after a successful call in __daemon() Makefile | 13 ++++++++++--- arch.c | 6 +++++- conf.c | 59 ++++++++++++++++++++++++++++++-------------------------- log.c | 10 +++++++--- passt.c | 9 ++++++--- pasta.c | 11 ++++++++--- pcap.c | 18 +++++++++++------ tap.c | 5 +++-- tcp.c | 12 +++++++++--- udp.c | 10 +++++----- util.c | 49 ++++++++++++++++++++++++++++++++++++---------- util.h | 5 +++++ 12 files changed, 141 insertions(+), 66 deletions(-) -- 2.43.0
We'll deprecate qrap(1) soon, and warnings reported by clang-tidy as
of LLVM versions 16 and later would need a bunch of changes there to
be addressed, mostly around CERT C rule ERR33-C and checking return
code from snprintf().
It makes no sense to fix warnings in qrap just for the sake of it, so
officially declare the bitrotting season open.
Signed-off-by: Stefano Brivio
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
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
We use fprintf() to print to standard output or standard error
streams. If something gets truncated or there's an output error, we
don't really want to try and report that, and at the same time it's
not abnormal behaviour upon which we should terminate, either.
Just silence the warning with an ugly FPRINTF() variadic macro casting
the fprintf() expressions to void.
Signed-off-by: Stefano Brivio
With clang-tidy and LLVM 19:
/home/sbrivio/passt/conf.c:1218:29: error: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses,-warnings-as-errors]
1218 | const char *octet = str + 3 * i;
| ^~~~~~
| ( )
/home/sbrivio/passt/ndp.c:285:18: error: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses,-warnings-as-errors]
285 | .len = 1 + 2 * n,
| ^~~~~~
| ( )
/home/sbrivio/passt/ndp.c:329:23: error: '%' has higher precedence than '-'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses,-warnings-as-errors]
329 | memset(ptr, 0, 8 - dns_s_len % 8); /* padding */
| ^~~~~~~~~~~~~~
| ( )
/home/sbrivio/passt/pcap.c:131:20: error: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses,-warnings-as-errors]
131 | pcap_frame(iov + i * frame_parts, frame_parts, offset, &now);
| ^~~~~~~~~~~~~~~~
| ( )
/home/sbrivio/passt/util.c:216:10: error: '/' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses,-warnings-as-errors]
216 | return (a->tv_nsec + 1000000000 - b->tv_nsec) / 1000 +
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ( )
/home/sbrivio/passt/util.c:217:10: error: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses,-warnings-as-errors]
217 | (a->tv_sec - b->tv_sec - 1) * 1000000;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ( )
/home/sbrivio/passt/util.c:220:9: error: '/' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses,-warnings-as-errors]
220 | return (a->tv_nsec - b->tv_nsec) / 1000 +
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ( )
/home/sbrivio/passt/util.c:221:9: error: '*' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses,-warnings-as-errors]
221 | (a->tv_sec - b->tv_sec) * 1000000;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| ( )
/home/sbrivio/passt/util.c:545:32: error: '/' has higher precedence than '+'; add parentheses to explicitly specify the order of operations [readability-math-missing-parentheses,-warnings-as-errors]
545 | return clone(fn, stack_area + stack_size / 2, flags, arg);
| ^~~~~~~~~~~~~~~
| ( )
Just... no.
Signed-off-by: Stefano Brivio
In pcap_init(), we open the packet capture file with O_CLOEXEC only
when possible.
In logfile_init() and pidfile_open(), the fact that we pass a third
'mode' argument to open() seems to confuse the android-cloexec-open
checker in LLVM versions from 16 to 19 (at least).
The checker is suggesting to add O_CLOEXEC to 'mode', and not in
'flags', where we already have it.
Signed-off-by: Stefano Brivio
On Fri, Oct 25, 2024 at 02:08:11PM +0200, Stefano Brivio wrote:
In pcap_init(), we open the packet capture file with O_CLOEXEC only when possible.
In logfile_init() and pidfile_open(), the fact that we pass a third 'mode' argument to open() seems to confuse the android-cloexec-open checker in LLVM versions from 16 to 19 (at least).
The checker is suggesting to add O_CLOEXEC to 'mode', and not in 'flags', where we already have it.
Signed-off-by: Stefano Brivio
--- log.c | 4 ++++ pcap.c | 1 + util.c | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/log.c b/log.c index 6932885..154466f 100644 --- a/log.c +++ b/log.c @@ -416,7 +416,11 @@ void logfile_init(const char *name, const char *path, size_t size) if (readlink("/proc/self/exe", exe, PATH_MAX - 1) < 0) die_perror("Failed to read own /proc/self/exe link");
+ /* We use O_CLOEXEC here, but clang-tidy as of LLVM 16 to 19 looks for + * it in the 'mode' argument if we have one, so... + */ log_file = open(path, O_CREAT | O_TRUNC | O_APPEND | O_RDWR | O_CLOEXEC, + /* NOLINTNEXTLINE(android-cloexec-open) */ S_IRUSR | S_IWUSR); if (log_file == -1) die_perror("Couldn't open log file %s", path); diff --git a/pcap.c b/pcap.c index 6ee6cdf..6753cfb 100644 --- a/pcap.c +++ b/pcap.c @@ -167,6 +167,7 @@ void pcap_init(struct ctx *c) return;
flags |= c->foreground ? O_CLOEXEC : 0; + /* NOLINTNEXTLINE(android-cloexec-open): ...only where possible */
Hmm... why do we need the conditional on c->foreground? It's close-on-exec(), not close-on-fork() or close-on-daemonize().
pcap_fd = open(c->pcap, flags, S_IRUSR | S_IWUSR); if (pcap_fd == -1) { perror("open"); diff --git a/util.c b/util.c index 9cb705e..75aee81 100644 --- a/util.c +++ b/util.c @@ -419,7 +419,11 @@ int pidfile_open(const char *path) if (!*path) return -1;
+ /* We use O_CLOEXEC here, but clang-tidy as of LLVM 16 to 19 looks for + * it in the 'mode' argument if we have one + */ if ((fd = open(path, O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, + /* NOLINTNEXTLINE(android-cloexec-open) */ S_IRUSR | S_IWUSR)) < 0) { perror("PID file open"); exit(EXIT_FAILURE);
-- 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
On Mon, 28 Oct 2024 12:00:53 +1100
David Gibson
On Fri, Oct 25, 2024 at 02:08:11PM +0200, Stefano Brivio wrote:
In pcap_init(), we open the packet capture file with O_CLOEXEC only when possible.
In logfile_init() and pidfile_open(), the fact that we pass a third 'mode' argument to open() seems to confuse the android-cloexec-open checker in LLVM versions from 16 to 19 (at least).
The checker is suggesting to add O_CLOEXEC to 'mode', and not in 'flags', where we already have it.
Signed-off-by: Stefano Brivio
--- log.c | 4 ++++ pcap.c | 1 + util.c | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/log.c b/log.c index 6932885..154466f 100644 --- a/log.c +++ b/log.c @@ -416,7 +416,11 @@ void logfile_init(const char *name, const char *path, size_t size) if (readlink("/proc/self/exe", exe, PATH_MAX - 1) < 0) die_perror("Failed to read own /proc/self/exe link");
+ /* We use O_CLOEXEC here, but clang-tidy as of LLVM 16 to 19 looks for + * it in the 'mode' argument if we have one, so... + */ log_file = open(path, O_CREAT | O_TRUNC | O_APPEND | O_RDWR | O_CLOEXEC, + /* NOLINTNEXTLINE(android-cloexec-open) */ S_IRUSR | S_IWUSR); if (log_file == -1) die_perror("Couldn't open log file %s", path); diff --git a/pcap.c b/pcap.c index 6ee6cdf..6753cfb 100644 --- a/pcap.c +++ b/pcap.c @@ -167,6 +167,7 @@ void pcap_init(struct ctx *c) return;
flags |= c->foreground ? O_CLOEXEC : 0; + /* NOLINTNEXTLINE(android-cloexec-open): ...only where possible */
Hmm... why do we need the conditional on c->foreground? It's close-on-exec(), not close-on-fork() or close-on-daemonize().
Oops, see also 20220823063151.854034-4-david@gibson.dropbear.id.au, I never learn. Fixing. -- Stefano
For clock_gettime(), we shouldn't ignore errors if they happen at
initialisation phase, because something is seriously wrong and it's
not helpful if we proceed as if nothing happened.
As we're up and running, though, it's probably better to report the
error and use a stale value than to terminate altogether. Make sure
we use a zero value if we don't have a stale one somewhere.
For timerfd_gettime() and timerfd_settime() failures, just report an
error, there isn't much else we can do.
Signed-off-by: Stefano Brivio
On Fri, Oct 25, 2024 at 02:08:12PM +0200, Stefano Brivio wrote:
For clock_gettime(), we shouldn't ignore errors if they happen at initialisation phase, because something is seriously wrong and it's not helpful if we proceed as if nothing happened.
As we're up and running, though, it's probably better to report the error and use a stale value than to terminate altogether. Make sure we use a zero value if we don't have a stale one somewhere.
For timerfd_gettime() and timerfd_settime() failures, just report an error, there isn't much else we can do.
Signed-off-by: Stefano Brivio
Reviewed-by: David Gibson
--- passt.c | 9 ++++++--- pcap.c | 17 +++++++++++------ tcp.c | 12 +++++++++--- 3 files changed, 26 insertions(+), 12 deletions(-)
diff --git a/passt.c b/passt.c index ad6f0bc..eaf231d 100644 --- a/passt.c +++ b/passt.c @@ -207,7 +207,8 @@ int main(int argc, char **argv) struct timespec now; struct sigaction sa;
- clock_gettime(CLOCK_MONOTONIC, &log_start); + if (clock_gettime(CLOCK_MONOTONIC, &log_start)) + die_perror("Failed to get CLOCK_MONOTONIC time");
arch_avx2_exec(argv);
@@ -265,7 +266,8 @@ int main(int argc, char **argv)
secret_init(&c);
- clock_gettime(CLOCK_MONOTONIC, &now); + if (clock_gettime(CLOCK_MONOTONIC, &now)) + die_perror("Failed to get CLOCK_MONOTONIC time");
flow_init();
@@ -313,7 +315,8 @@ loop: if (nfds == -1 && errno != EINTR) die_perror("epoll_wait() failed in main loop");
- clock_gettime(CLOCK_MONOTONIC, &now); + if (clock_gettime(CLOCK_MONOTONIC, &now)) + err_perror("Failed to get CLOCK_MONOTONIC time");
for (i = 0; i < nfds; i++) { union epoll_ref ref = *((union epoll_ref *)&events[i].data.u64); diff --git a/pcap.c b/pcap.c index 6753cfb..cab813f 100644 --- a/pcap.c +++ b/pcap.c @@ -100,12 +100,14 @@ static void pcap_frame(const struct iovec *iov, size_t iovcnt, void pcap(const char *pkt, size_t l2len) { struct iovec iov = { (char *)pkt, l2len }; - struct timespec now; + struct timespec now = { 0 };
if (pcap_fd == -1) return;
- clock_gettime(CLOCK_REALTIME, &now); + if (clock_gettime(CLOCK_REALTIME, &now)) + err_perror("Failed to get CLOCK_REALTIME time"); + pcap_frame(&iov, 1, 0, &now); }
@@ -119,13 +121,14 @@ void pcap(const char *pkt, size_t l2len) void pcap_multiple(const struct iovec *iov, size_t frame_parts, unsigned int n, size_t offset) { - struct timespec now; + struct timespec now = { 0 }; unsigned int i;
if (pcap_fd == -1) return;
- clock_gettime(CLOCK_REALTIME, &now); + if (clock_gettime(CLOCK_REALTIME, &now)) + err_perror("Failed to get CLOCK_REALTIME time");
for (i = 0; i < n; i++) pcap_frame(iov + i * frame_parts, frame_parts, offset, &now); @@ -143,12 +146,14 @@ void pcap_multiple(const struct iovec *iov, size_t frame_parts, unsigned int n, /* cppcheck-suppress unusedFunction */ void pcap_iov(const struct iovec *iov, size_t iovcnt, size_t offset) { - struct timespec now; + struct timespec now = { 0 };
if (pcap_fd == -1) return;
- clock_gettime(CLOCK_REALTIME, &now); + if (clock_gettime(CLOCK_REALTIME, &now)) + err_perror("Failed to get CLOCK_REALTIME time"); + pcap_frame(iov, iovcnt, offset, &now); }
diff --git a/tcp.c b/tcp.c index 0d22e07..d55caee 100644 --- a/tcp.c +++ b/tcp.c @@ -548,7 +548,8 @@ static void tcp_timer_ctl(const struct ctx *c, struct tcp_tap_conn *conn) (unsigned long long)it.it_value.tv_sec, (unsigned long long)it.it_value.tv_nsec / 1000 / 1000);
- timerfd_settime(conn->timer, 0, &it, NULL); + if (timerfd_settime(conn->timer, 0, &it, NULL)) + flow_err(conn, "failed to set timer: %s", strerror(errno)); }
/** @@ -2240,7 +2241,9 @@ void tcp_timer_handler(const struct ctx *c, union epoll_ref ref) * timer is currently armed, this event came from a previous setting, * and we just set the timer to a new point in the future: discard it. */ - timerfd_gettime(conn->timer, &check_armed); + if (timerfd_gettime(conn->timer, &check_armed)) + flow_err(conn, "failed to read timer: %s", strerror(errno)); + if (check_armed.it_value.tv_sec || check_armed.it_value.tv_nsec) return;
@@ -2278,7 +2281,10 @@ void tcp_timer_handler(const struct ctx *c, union epoll_ref ref) * case. This avoids having to preemptively reset the timer on * ~ACK_TO_TAP_DUE or ~ACK_FROM_TAP_DUE. */ - timerfd_settime(conn->timer, 0, &new, &old); + if (timerfd_settime(conn->timer, 0, &new, &old)) + flow_err(conn, "failed to set timer: %s", + strerror(errno)); + if (old.it_value.tv_sec == ACT_TIMEOUT) { flow_dbg(conn, "activity timeout"); tcp_rst(c, conn);
-- 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
/home/sbrivio/passt/udp.c:171:1: error: inital values in enum 'udp_iov_idx' are not consistent, consider explicit initialization of all, none or only the first enumerator [cert-int09-c,readability-enum-initial-value,-warnings-as-errors]
171 | enum udp_iov_idx {
| ^
172 | UDP_IOV_TAP = 0,
173 | UDP_IOV_ETH = 1,
174 | UDP_IOV_IP = 2,
175 | UDP_IOV_PAYLOAD = 3,
176 | UDP_NUM_IOVS
|
| = 4
Don't initialise any value, so that it's obvious that constants map to
unique values.
Signed-off-by: Stefano Brivio
On Fri, Oct 25, 2024 at 02:08:13PM +0200, Stefano Brivio wrote:
/home/sbrivio/passt/udp.c:171:1: error: inital values in enum 'udp_iov_idx' are not consistent, consider explicit initialization of all, none or only the first enumerator [cert-int09-c,readability-enum-initial-value,-warnings-as-errors] 171 | enum udp_iov_idx { | ^ 172 | UDP_IOV_TAP = 0, 173 | UDP_IOV_ETH = 1, 174 | UDP_IOV_IP = 2, 175 | UDP_IOV_PAYLOAD = 3, 176 | UDP_NUM_IOVS | | = 4
Don't initialise any value, so that it's obvious that constants map to unique values.
Signed-off-by: Stefano Brivio
Reviewed-by: David Gibson
--- udp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/udp.c b/udp.c index 100610f..0c01067 100644 --- a/udp.c +++ b/udp.c @@ -169,11 +169,11 @@ udp_meta[UDP_MAX_FRAMES]; * @UDP_NUM_IOVS the number of entries in the iovec array */ enum udp_iov_idx { - UDP_IOV_TAP = 0, - UDP_IOV_ETH = 1, - UDP_IOV_IP = 2, - UDP_IOV_PAYLOAD = 3, - UDP_NUM_IOVS + UDP_IOV_TAP, + UDP_IOV_ETH, + UDP_IOV_IP, + UDP_IOV_PAYLOAD, + UDP_NUM_IOVS, };
/* IOVs and msghdr arrays for receiving datagrams from sockets */
-- 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
I thought we could just set errno to 0, do a bunch of stuff, and check
that errno didn't change to infer we succeeded. But clang-tidy,
starting with LLVM 19, reports:
/home/sbrivio/passt/util.c:465:6: error: An undefined value may be read from 'errno' [clang-analyzer-unix.Errno,-warnings-as-errors]
465 | if (errno)
| ^
/usr/include/errno.h:38:16: note: expanded from macro 'errno'
38 | # define errno (*__errno_location ())
| ^~~~~~~~~~~~~~~~~~~~~~
/home/sbrivio/passt/util.c:446:6: note: Assuming the condition is false
446 | if (pid == -1) {
| ^~~~~~~~~
/home/sbrivio/passt/util.c:446:2: note: Taking false branch
446 | if (pid == -1) {
| ^
/home/sbrivio/passt/util.c:451:6: note: Assuming 'pid' is 0
451 | if (pid) {
| ^~~
/home/sbrivio/passt/util.c:451:2: note: Taking false branch
451 | if (pid) {
| ^
/home/sbrivio/passt/util.c:463:2: note: Assuming that 'close' is successful; 'errno' becomes undefined after the call
463 | close(devnull_fd);
| ^~~~~~~~~~~~~~~~~
/home/sbrivio/passt/util.c:465:6: note: An undefined value may be read from 'errno'
465 | if (errno)
| ^
/usr/include/errno.h:38:16: note: expanded from macro 'errno'
38 | # define errno (*__errno_location ())
| ^~~~~~~~~~~~~~~~~~~~~~
And the LLVM documentation for the unix.Errno checker, 1.1.8.3
unix.Errno (C), mentions, at:
https://clang.llvm.org/docs/analyzer/checkers.html#unix-errno
that:
The C and POSIX standards often do not define if a standard library
function may change value of errno if the call does not fail.
Therefore, errno should only be used if it is known from the return
value of a function that the call has failed.
which is, somewhat surprisingly, the case for close().
Instead of using errno, check the actual return values of the calls
we issue here.
Signed-off-by: Stefano Brivio
participants (2)
-
David Gibson
-
Stefano Brivio