[PATCH v3 00/12] Improvements to static checker invocation
While working on pesto, I ran into a number of awkward errors with the static checkers. This series reworks the invocation of the checkers in a way that will let us deal with that. As a bonus, it also gives us static checking for passt-repair. It also a number of other cleanups to the Makefile that seemed natural along the way. v3: - Rework changes to $(FLAGS) so they're much less likely to have side effects we're not ready for. v2: - Fixed nasty test failure in test/build/build.py David Gibson (12): Makefile: Use make variables for static checker configuration Makefile: Make conditional definition of $(BIN) clearer Makefile: Use common binary compilation rule Makefile: Remove unhelpful $(HEADERS) variable Makefile: Add header dependencies for secondary binaries Makefile: Split $(FLAGS) into cpp and cc components cppcheck, clang-tidy: Static checkers don't need non-preprocessor flags Makefile: Split static checker targets passt-repair: Split out inotify handling to its own function passt-repair: Simplify construction of Unix path from inotify passt-repair: Run static checkers pesto: Run static checkers on pesto sources Makefile | 135 ++++++++++++++++++++++++-------------- linux_dep.h | 2 +- passt-repair.c | 171 +++++++++++++++++++++++++++---------------------- pesto.c | 1 - 4 files changed, 182 insertions(+), 127 deletions(-) -- 2.54.0
Our cppcheck and clang-tidy rules don't really follow normal Makefile
conventions. Usually any commands other than the very basics have their
binary specified in a variable so it can be overridden on the command line
if they're in an unusual location. Implement that for $(CPPCHECK) and
$(CLANG_TIDY)
Likewise flags to tools usually have their own Make variable. Do the same
with $(CLANG_TIDY_FLAGS) and $(CPPCHECK_FLAGS). Note that these only have
the options specifically for the static checker, not compiler flags which
we are also supplying to the static checker - those are derived from
FLAGS / CFLAGS / CPPFLAGS as before.
As part of that we change the probing for --check-level=exhaustive from
being run as part of the cppcheck target, to being run when we build the
CPPCHECK_FLAGS variable. That doesn't make any real difference now, but
will make things nicer if we need multiple cppcheck targets in future (e.g.
for passt-repair).
Signed-off-by: David Gibson
Currently we pass all our compiler flags to clang-tidy, except -pie, which
it won't accept. In fact in order to run the checker, we only need the
preprocessor flags. Simplify the command line by passing only those.
For cppcheck we already filter out just -D options from the compiler flags.
Simplify this by only passing preprocessor flags, now that we've split
those out into their own variables. Furthermore, one of cppcheck's
features which we're currently not exploiting is to check multiple / all
preprocessor option combinations in a single pass. Therefore, pass only
$(BASE_CPPFLAGS), which contains the mandatory options with which we can't
compile at all.
While we're there remove a redundant $^ that slipped in at some point.
Signed-off-by: David Gibson
The list of binaries is dependent on the target architecture, because
x86_64 adds the passt.avx2 and pasta.avx2 binaries. Make this more
obvious by defining BIN in common, then augmenting it in the x86_64
case.
Signed-off-by: David Gibson
The $(FLAGS) variable contains mandatory compiler flags that should not be
overridden. However, it contains a mixture of flags for the preprocessor
and for the compiler proper. That's causing some inconvenience for other
Makefile cleanups, so split it into $(BASE_CPPFLAGS) and $(BASE_CFLAGS)
variables.
Signed-off-by: David Gibson
Currently we have a single 'cppcheck' and 'clang-tidy' target which checks
passt. However, it doesn't check the additional binaries, qrap and
passt-repair. In preparation for running the static checkers on those as
well, split the targets into a top-level rule and a pattern rule which we
will be able to reuse.
Signed-off-by: David Gibson
PASST_HEADERS contains all the headers used by passt, which we use in
various dependencies. However, qrap and passt-repair each use several
headers which we don't have dependencies for. Add handling for this to the
Makefile.
Signed-off-by: David Gibson
Update the Makefile to run both clang-tidy and cppcheck on pesto as well
as on passt and passt-repair. This requires a couple of secondary
corrections:
* pesto.c had an inline suppression that is no longer correct now that
the protocol version has been bumped to 1. Remove it.
* We were globally suppressing the unusedStructMember because it
hit many false positives on both passt and passt-repair. It doesn't
in pesto, meaning it instead creates an unusedSuppression warning.
Apply the suppression as a flag override for passt and passt-repair,
instead of globally.
Signed-off-by: David Gibson
passt-repair can operate two ways: either it can be given an explicit
socket path to connect to, or it can be given a directory. In the second
mode, it will wait for a socket to appear in that directory before
connecting to it.
That waiting involves some inotify logic that is essentially unrelated to
the rest of the code. However, it's currently inline in main() making that
very long. Moreover, the block handling inotify shadows several variables
used in the rest of main() which will make static checkers complain once
we get them running on passt-repair.
Address this by moving the inotify handling into its own function.
Signed-off-by: David Gibson
When passt-repair is invoked with a directory name, it waits for a Unix
socket to appear in that directory. We need to build the Unix path name
from the given directory, plus the stem file name from the inotify event.
Currently, we build that path into a temporary buffer of size PATH_MAX,
then move it into the smaller buffer inside the Unix sockaddr. There's no
particular reason for this two step process, we can build the address
directly within the sockaddr_un. This will give a slightly different error
if the constructed path exceeds the maximum length of a Unix address, but
it will fail either way so it doesn't really matter.
Signed-off-by: David Gibson
Run the static checkers, cppcheck and clang-tidy on passt-repair as well
as on passt proper. This shows up handful of remaining minor warnings,
which we correct.
Signed-off-by: David Gibson
On Tue, 12 May 2026 15:52:50 +1000
David Gibson
The $(FLAGS) variable contains mandatory compiler flags that should not be overridden. However, it contains a mixture of flags for the preprocessor and for the compiler proper. That's causing some inconvenience for other Makefile cleanups, so split it into $(BASE_CPPFLAGS) and $(BASE_CFLAGS) variables.
Signed-off-by: David Gibson
--- Makefile | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index a8f8d06e..697f229f 100644 --- a/Makefile +++ b/Makefile @@ -30,12 +30,17 @@ ifeq ($(shell $(CC) -O2 -dM -E - < /dev/null 2>&1 | grep ' _FORTIFY_SOURCE ' > / FORTIFY_FLAG := -D_FORTIFY_SOURCE=2 endif
-FLAGS := -Wall -Wextra -Wno-format-zero-length -Wformat-security -FLAGS += -pedantic -std=c11 -D_XOPEN_SOURCE=700 -D_GNU_SOURCE -FLAGS += $(FORTIFY_FLAG) -O2 -pie -fPIE -FLAGS += -DPAGE_SIZE=$(shell getconf PAGE_SIZE) -FLAGS += -DVERSION=\"$(VERSION)\" -FLAGS += -DDUAL_STACK_SOCKETS=$(DUAL_STACK_SOCKETS) +# Mandatory preprocessor flags that won't be overridden with $(CPPFLAGS) +# FIXME: Could some of these be default, rather than required? +BASE_CPPFLAGS := -D_XOPEN_SOURCE=700 -D_GNU_SOURCE $(FORTIFY_FLAG) +BASE_CPPFLAGS += -DPAGE_SIZE=$(shell getconf PAGE_SIZE) +BASE_CPPFLAGS += -DVERSION=\"$(VERSION)\" +BASE_CPPFLAGS += -DDUAL_STACK_SOCKETS=$(DUAL_STACK_SOCKETS) + +# Mandatory compiler flags that won't be overridden with $(CFLAGS) +# FIXME: Could some of these be default, rather than required? +BASE_CFLAGS := -std=c11 -pie -fPIE -O2 +BASE_CFLAGS += -pedantic -Wall -Wextra -Wno-format-zero-length -Wformat-security
This new version of the series looks good to me in general (minus potential concern reported below), and everything seems to work on Debian and Fedora, but I would still like to try things out on Alpine or Void Linux because musl might cause surprises. I haven't got to it yet. Meanwhile, regarding these FIXME comments: I think it *is* currently possible to override those flags (with different values for the same options), and overriding -D_FORTIFY_SOURCE on openSUSE (I haven't tried right now) was the initial motivation behind FLAGS. That is, the overriding role of CFLAGS seems to be preserved for these BASE_* flags as well, because $CFLAGS is given to the compiler after $BASE_CPPFLAGS, $CPPFLAGS, and $BASE_CFLAGS. So, in this sense, I would already call them "default" flags. If that's the case, I think it's fine. Otherwise we need to find another solution at least for the short term. By the way, if it helps addressing those comments at some point (I would apply anyway this series meanwhile if I don't find breakages, because not being able to run static checkers automatically on pesto is pretty nasty), out of those flags: * -D_XOPEN_SOURCE, -D_GNU_SOURCE, and -DPAGE_SIZE are strictly required to build (at least in some environments) * -D_FORTIFY_SOURCE, -pie, -fPIE are not required to build but they are critical for security * -DVERSION is not required to build but makes things confusing and issues hard to debug because the version (usually supplied by the distribution) isn't reported in logs and logs of other tools - -DDUAL_STACK_SOCKETS doesn't seem to be used anymore starting from commit b8d4fac6a2e7 ("util, pif: Replace sock_l4() with pif_sock_l4()")... was it intended, actually? - -std=c11 is strictly required to ensure we build things correctly - -O2 is optional, but dropping it (by default) might require annoying adjustments in distributions - -pedantic, -Wall, -Wextra, -Wno-format-zero-length, -Wformat-security are all optional and useful for development (including distribution development), and might be security relevant in some cases -- Stefano
On Wed, May 13, 2026 at 09:11:00AM +0200, Stefano Brivio wrote:
On Tue, 12 May 2026 15:52:50 +1000 David Gibson
wrote: The $(FLAGS) variable contains mandatory compiler flags that should not be overridden. However, it contains a mixture of flags for the preprocessor and for the compiler proper. That's causing some inconvenience for other Makefile cleanups, so split it into $(BASE_CPPFLAGS) and $(BASE_CFLAGS) variables.
Signed-off-by: David Gibson
--- Makefile | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index a8f8d06e..697f229f 100644 --- a/Makefile +++ b/Makefile @@ -30,12 +30,17 @@ ifeq ($(shell $(CC) -O2 -dM -E - < /dev/null 2>&1 | grep ' _FORTIFY_SOURCE ' > / FORTIFY_FLAG := -D_FORTIFY_SOURCE=2 endif
-FLAGS := -Wall -Wextra -Wno-format-zero-length -Wformat-security -FLAGS += -pedantic -std=c11 -D_XOPEN_SOURCE=700 -D_GNU_SOURCE -FLAGS += $(FORTIFY_FLAG) -O2 -pie -fPIE -FLAGS += -DPAGE_SIZE=$(shell getconf PAGE_SIZE) -FLAGS += -DVERSION=\"$(VERSION)\" -FLAGS += -DDUAL_STACK_SOCKETS=$(DUAL_STACK_SOCKETS) +# Mandatory preprocessor flags that won't be overridden with $(CPPFLAGS) +# FIXME: Could some of these be default, rather than required? +BASE_CPPFLAGS := -D_XOPEN_SOURCE=700 -D_GNU_SOURCE $(FORTIFY_FLAG) +BASE_CPPFLAGS += -DPAGE_SIZE=$(shell getconf PAGE_SIZE) +BASE_CPPFLAGS += -DVERSION=\"$(VERSION)\" +BASE_CPPFLAGS += -DDUAL_STACK_SOCKETS=$(DUAL_STACK_SOCKETS) + +# Mandatory compiler flags that won't be overridden with $(CFLAGS) +# FIXME: Could some of these be default, rather than required? +BASE_CFLAGS := -std=c11 -pie -fPIE -O2 +BASE_CFLAGS += -pedantic -Wall -Wextra -Wno-format-zero-length -Wformat-security
This new version of the series looks good to me in general (minus potential concern reported below), and everything seems to work on Debian and Fedora, but I would still like to try things out on Alpine or Void Linux because musl might cause surprises. I haven't got to it yet.
Makes sense, though I'm pretty sure it will be safe. I considered having some later patches that started removing non-essential things from BASE_*FLAGS. Those would certainly have required careful testing across distros. But working out what should move became more complicated than I initially thought (that's basically the discussion later in this mail), so I postponed it.
Meanwhile, regarding these FIXME comments: I think it *is* currently possible to override those flags (with different values for the same options), and overriding -D_FORTIFY_SOURCE on openSUSE (I haven't tried right now) was the initial motivation behind FLAGS.
True, I was thinking "overridden" in the make variable sense, but at the actual compiler that's misleading.
That is, the overriding role of CFLAGS seems to be preserved for these BASE_* flags as well, because $CFLAGS is given to the compiler after $BASE_CPPFLAGS, $CPPFLAGS, and $BASE_CFLAGS. So, in this sense, I would already call them "default" flags.
Right, it's kind of the subtle distinction between two types of default: default unless that specific option is overridden (BASE_CFLAGS), or default unless all the "default" options are overridden as a block (CFLAGS).
If that's the case, I think it's fine. Otherwise we need to find another solution at least for the short term.
By the way, if it helps addressing those comments at some point (I would apply anyway this series meanwhile if I don't find breakages, because not being able to run static checkers automatically on pesto is pretty nasty), out of those flags:
* -D_XOPEN_SOURCE, -D_GNU_SOURCE, and -DPAGE_SIZE are strictly required to build (at least in some environments)
Yes.
* -D_FORTIFY_SOURCE, -pie, -fPIE are not required to build but they are critical for security
Right. I'd kind of consider -pie and -fPIE in a different category than "flags", since they're choosing the kind of output object we're generating.
* -DVERSION is not required to build but makes things confusing and issues hard to debug because the version (usually supplied by the distribution) isn't reported in logs and logs of other tools
Right. I guess I'd also consider it mandatory. The fact that you can sort of get away without it seems more an accident of how we include it, rather than policy.
- -DDUAL_STACK_SOCKETS doesn't seem to be used anymore starting from commit b8d4fac6a2e7 ("util, pif: Replace sock_l4() with pif_sock_l4()")... was it intended, actually?
Huh, good point. I'm not sure it was exactly intentional. I was certainly grappling with the fact that in a sense it sometimes wanted to return two sockets in the !DUAL_STACK_SOCKETS case was really awkward. My plan, as best I recall was to move the handling of that case up layers, until it reached what would eventually become the forward table - so separate forwarding entries for v4 and v6 if we don't have dual stack sockets. But, since we introduced DUAL_STACK_SOCKETS, I've discovered that the dual stack socket interface is described by RFC and supported by BSD and Windows as well as Linux. I'm pretty happy killing this define.
- -std=c11 is strictly required to ensure we build things correctly
Huh, is it? I would have thought it just catches us from using any C23 or GNU extensions.
- -O2 is optional, but dropping it (by default) might require annoying adjustments in distributions
Right. But on the other hand, overriding "all" packages with the distro preferred optimization flags is one of the main uses for distro CFLAGS overrides. Not sure what to do about this one.
- -pedantic, -Wall, -Wextra, -Wno-format-zero-length, -Wformat-security are all optional and useful for development (including distribution development), and might be security relevant in some cases
They're security relevant in the sense that it's good for us to get the warnings to catch possible security bugs. But a tree that builds clean with the flags shouldn't have different security properties if it's built without them. So I think it only matters that *we* use the flags, we don't need to impose it on anything else. But, there's an awkward gotcha here. The build.py testcase needs -Werror to catch warnings, but obviously we don't want it to have to reiterate all the other warning flags. If we put these all into CFLAGS, it's tricky for build.py to just add -Werror without touching the rest. From our point of view, I like the idea of adding -Werror to the default CFLAGS, which fixes that problem. But as you pointed out when I did that in the earlier version that might break distro builds causing use more trouble to fix it. -- 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 Thu, 14 May 2026 12:01:24 +1000
David Gibson
On Wed, May 13, 2026 at 09:11:00AM +0200, Stefano Brivio wrote:
On Tue, 12 May 2026 15:52:50 +1000 David Gibson
wrote: The $(FLAGS) variable contains mandatory compiler flags that should not be overridden. However, it contains a mixture of flags for the preprocessor and for the compiler proper. That's causing some inconvenience for other Makefile cleanups, so split it into $(BASE_CPPFLAGS) and $(BASE_CFLAGS) variables.
Signed-off-by: David Gibson
--- Makefile | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index a8f8d06e..697f229f 100644 --- a/Makefile +++ b/Makefile @@ -30,12 +30,17 @@ ifeq ($(shell $(CC) -O2 -dM -E - < /dev/null 2>&1 | grep ' _FORTIFY_SOURCE ' > / FORTIFY_FLAG := -D_FORTIFY_SOURCE=2 endif
-FLAGS := -Wall -Wextra -Wno-format-zero-length -Wformat-security -FLAGS += -pedantic -std=c11 -D_XOPEN_SOURCE=700 -D_GNU_SOURCE -FLAGS += $(FORTIFY_FLAG) -O2 -pie -fPIE -FLAGS += -DPAGE_SIZE=$(shell getconf PAGE_SIZE) -FLAGS += -DVERSION=\"$(VERSION)\" -FLAGS += -DDUAL_STACK_SOCKETS=$(DUAL_STACK_SOCKETS) +# Mandatory preprocessor flags that won't be overridden with $(CPPFLAGS) +# FIXME: Could some of these be default, rather than required? +BASE_CPPFLAGS := -D_XOPEN_SOURCE=700 -D_GNU_SOURCE $(FORTIFY_FLAG) +BASE_CPPFLAGS += -DPAGE_SIZE=$(shell getconf PAGE_SIZE) +BASE_CPPFLAGS += -DVERSION=\"$(VERSION)\" +BASE_CPPFLAGS += -DDUAL_STACK_SOCKETS=$(DUAL_STACK_SOCKETS) + +# Mandatory compiler flags that won't be overridden with $(CFLAGS) +# FIXME: Could some of these be default, rather than required? +BASE_CFLAGS := -std=c11 -pie -fPIE -O2 +BASE_CFLAGS += -pedantic -Wall -Wextra -Wno-format-zero-length -Wformat-security
This new version of the series looks good to me in general (minus potential concern reported below), and everything seems to work on Debian and Fedora, but I would still like to try things out on Alpine or Void Linux because musl might cause surprises. I haven't got to it yet.
Makes sense, though I'm pretty sure it will be safe. I considered having some later patches that started removing non-essential things from BASE_*FLAGS. Those would certainly have required careful testing across distros. But working out what should move became more complicated than I initially thought (that's basically the discussion later in this mail), so I postponed it.
If you plan to submit another series soon, note that I don't plan to re-do the testing I'm currently doing, though, so you should plan to do those yourself and work with maintainers if things break, in case.
Meanwhile, regarding these FIXME comments: I think it *is* currently possible to override those flags (with different values for the same options), and overriding -D_FORTIFY_SOURCE on openSUSE (I haven't tried right now) was the initial motivation behind FLAGS.
True, I was thinking "overridden" in the make variable sense, but at the actual compiler that's misleading.
That is, the overriding role of CFLAGS seems to be preserved for these BASE_* flags as well, because $CFLAGS is given to the compiler after $BASE_CPPFLAGS, $CPPFLAGS, and $BASE_CFLAGS. So, in this sense, I would already call them "default" flags.
Right, it's kind of the subtle distinction between two types of default: default unless that specific option is overridden (BASE_CFLAGS), or default unless all the "default" options are overridden as a block (CFLAGS).
If that's the case, I think it's fine. Otherwise we need to find another solution at least for the short term.
By the way, if it helps addressing those comments at some point (I would apply anyway this series meanwhile if I don't find breakages, because not being able to run static checkers automatically on pesto is pretty nasty), out of those flags:
* -D_XOPEN_SOURCE, -D_GNU_SOURCE, and -DPAGE_SIZE are strictly required to build (at least in some environments)
Yes.
* -D_FORTIFY_SOURCE, -pie, -fPIE are not required to build but they are critical for security
Right. I'd kind of consider -pie and -fPIE in a different category than "flags", since they're choosing the kind of output object we're generating.
* -DVERSION is not required to build but makes things confusing and issues hard to debug because the version (usually supplied by the distribution) isn't reported in logs and logs of other tools
Right. I guess I'd also consider it mandatory. The fact that you can sort of get away without it seems more an accident of how we include it, rather than policy.
- -DDUAL_STACK_SOCKETS doesn't seem to be used anymore starting from commit b8d4fac6a2e7 ("util, pif: Replace sock_l4() with pif_sock_l4()")... was it intended, actually?
Huh, good point. I'm not sure it was exactly intentional. I was certainly grappling with the fact that in a sense it sometimes wanted to return two sockets in the !DUAL_STACK_SOCKETS case was really awkward. My plan, as best I recall was to move the handling of that case up layers, until it reached what would eventually become the forward table - so separate forwarding entries for v4 and v6 if we don't have dual stack sockets.
But, since we introduced DUAL_STACK_SOCKETS, I've discovered that the dual stack socket interface is described by RFC and supported by BSD and Windows as well as Linux. I'm pretty happy killing this define.
- -std=c11 is strictly required to ensure we build things correctly
Huh, is it? I would have thought it just catches us from using any C23 or GNU extensions.
It is because, at least in theory, while some constructs we use might be valid C99 or C17, we don't have guarantees that they have the same semantics. I don't see it happening in practice but I wouldn't risk it. Also mind that building with -std=c99 doesn't work (anymore).
- -O2 is optional, but dropping it (by default) might require annoying adjustments in distributions
Right. But on the other hand, overriding "all" packages with the distro preferred optimization flags is one of the main uses for distro CFLAGS overrides. Not sure what to do about this one.
Leave it like it is because it works as intended. It's our default which is also the default for most distributions, and Gentoo users (it's pretty much about them) can still override it by passing -O3 later.
- -pedantic, -Wall, -Wextra, -Wno-format-zero-length, -Wformat-security are all optional and useful for development (including distribution development), and might be security relevant in some cases
They're security relevant in the sense that it's good for us to get the warnings to catch possible security bugs. But a tree that builds clean with the flags shouldn't have different security properties if it's built without them. So I think it only matters that *we* use the flags, we don't need to impose it on anything else.
See above: "(including distribution development)". If those aren't there, we won't get some warnings from building distribution packages, and a few of those were useful in the past.
But, there's an awkward gotcha here. The build.py testcase needs -Werror to catch warnings, but obviously we don't want it to have to reiterate all the other warning flags. If we put these all into CFLAGS, it's tricky for build.py to just add -Werror without touching the rest.
My whole point was / is that CFLAGS needs to *add* to the existing flags, including warnings, instead of replacing them. You can override warnings flags too by appending options.
From our point of view, I like the idea of adding -Werror to the default CFLAGS, which fixes that problem.
At the moment there's no problem though.
But as you pointed out when I did that in the earlier version that might break distro builds causing use more trouble to fix it.
That *would* break distribution builds at the moment, so it's not acceptable. I constantly have fixes for warnings I spot in the packages I build on my to-do list. -- Stefano
On Thu, May 14, 2026 at 11:41:02AM +0200, Stefano Brivio wrote:
On Thu, 14 May 2026 12:01:24 +1000 David Gibson
wrote: On Wed, May 13, 2026 at 09:11:00AM +0200, Stefano Brivio wrote:
On Tue, 12 May 2026 15:52:50 +1000 David Gibson
wrote: The $(FLAGS) variable contains mandatory compiler flags that should not be overridden. However, it contains a mixture of flags for the preprocessor and for the compiler proper. That's causing some inconvenience for other Makefile cleanups, so split it into $(BASE_CPPFLAGS) and $(BASE_CFLAGS) variables.
Signed-off-by: David Gibson
--- Makefile | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index a8f8d06e..697f229f 100644 --- a/Makefile +++ b/Makefile @@ -30,12 +30,17 @@ ifeq ($(shell $(CC) -O2 -dM -E - < /dev/null 2>&1 | grep ' _FORTIFY_SOURCE ' > / FORTIFY_FLAG := -D_FORTIFY_SOURCE=2 endif
-FLAGS := -Wall -Wextra -Wno-format-zero-length -Wformat-security -FLAGS += -pedantic -std=c11 -D_XOPEN_SOURCE=700 -D_GNU_SOURCE -FLAGS += $(FORTIFY_FLAG) -O2 -pie -fPIE -FLAGS += -DPAGE_SIZE=$(shell getconf PAGE_SIZE) -FLAGS += -DVERSION=\"$(VERSION)\" -FLAGS += -DDUAL_STACK_SOCKETS=$(DUAL_STACK_SOCKETS) +# Mandatory preprocessor flags that won't be overridden with $(CPPFLAGS) +# FIXME: Could some of these be default, rather than required? +BASE_CPPFLAGS := -D_XOPEN_SOURCE=700 -D_GNU_SOURCE $(FORTIFY_FLAG) +BASE_CPPFLAGS += -DPAGE_SIZE=$(shell getconf PAGE_SIZE) +BASE_CPPFLAGS += -DVERSION=\"$(VERSION)\" +BASE_CPPFLAGS += -DDUAL_STACK_SOCKETS=$(DUAL_STACK_SOCKETS) + +# Mandatory compiler flags that won't be overridden with $(CFLAGS) +# FIXME: Could some of these be default, rather than required? +BASE_CFLAGS := -std=c11 -pie -fPIE -O2 +BASE_CFLAGS += -pedantic -Wall -Wextra -Wno-format-zero-length -Wformat-security
This new version of the series looks good to me in general (minus potential concern reported below), and everything seems to work on Debian and Fedora, but I would still like to try things out on Alpine or Void Linux because musl might cause surprises. I haven't got to it yet.
Makes sense, though I'm pretty sure it will be safe. I considered having some later patches that started removing non-essential things from BASE_*FLAGS. Those would certainly have required careful testing across distros. But working out what should move became more complicated than I initially thought (that's basically the discussion later in this mail), so I postponed it.
If you plan to submit another series soon, note that I don't plan to re-do the testing I'm currently doing, though, so you should plan to do those yourself and work with maintainers if things break, in case.
Understood, but I don't plan to resume this particularly soon.
Meanwhile, regarding these FIXME comments: I think it *is* currently possible to override those flags (with different values for the same options), and overriding -D_FORTIFY_SOURCE on openSUSE (I haven't tried right now) was the initial motivation behind FLAGS.
True, I was thinking "overridden" in the make variable sense, but at the actual compiler that's misleading.
That is, the overriding role of CFLAGS seems to be preserved for these BASE_* flags as well, because $CFLAGS is given to the compiler after $BASE_CPPFLAGS, $CPPFLAGS, and $BASE_CFLAGS. So, in this sense, I would already call them "default" flags.
Right, it's kind of the subtle distinction between two types of default: default unless that specific option is overridden (BASE_CFLAGS), or default unless all the "default" options are overridden as a block (CFLAGS).
If that's the case, I think it's fine. Otherwise we need to find another solution at least for the short term.
By the way, if it helps addressing those comments at some point (I would apply anyway this series meanwhile if I don't find breakages, because not being able to run static checkers automatically on pesto is pretty nasty), out of those flags:
* -D_XOPEN_SOURCE, -D_GNU_SOURCE, and -DPAGE_SIZE are strictly required to build (at least in some environments)
Yes.
* -D_FORTIFY_SOURCE, -pie, -fPIE are not required to build but they are critical for security
Right. I'd kind of consider -pie and -fPIE in a different category than "flags", since they're choosing the kind of output object we're generating.
* -DVERSION is not required to build but makes things confusing and issues hard to debug because the version (usually supplied by the distribution) isn't reported in logs and logs of other tools
Right. I guess I'd also consider it mandatory. The fact that you can sort of get away without it seems more an accident of how we include it, rather than policy.
- -DDUAL_STACK_SOCKETS doesn't seem to be used anymore starting from commit b8d4fac6a2e7 ("util, pif: Replace sock_l4() with pif_sock_l4()")... was it intended, actually?
Huh, good point. I'm not sure it was exactly intentional. I was certainly grappling with the fact that in a sense it sometimes wanted to return two sockets in the !DUAL_STACK_SOCKETS case was really awkward. My plan, as best I recall was to move the handling of that case up layers, until it reached what would eventually become the forward table - so separate forwarding entries for v4 and v6 if we don't have dual stack sockets.
But, since we introduced DUAL_STACK_SOCKETS, I've discovered that the dual stack socket interface is described by RFC and supported by BSD and Windows as well as Linux. I'm pretty happy killing this define.
- -std=c11 is strictly required to ensure we build things correctly
Huh, is it? I would have thought it just catches us from using any C23 or GNU extensions.
It is because, at least in theory, while some constructs we use might be valid C99 or C17, we don't have guarantees that they have the same semantics. I don't see it happening in practice but I wouldn't risk it.
I mean... the C committee has done some weird stuff, but changing the behaviour of existing valid constructs seems well beyond the pale.
Also mind that building with -std=c99 doesn't work (anymore).
Certainly - at this point it's a constraint that CC refer to a C11 compiler, not just a C compiler.
- -O2 is optional, but dropping it (by default) might require annoying adjustments in distributions
Right. But on the other hand, overriding "all" packages with the distro preferred optimization flags is one of the main uses for distro CFLAGS overrides. Not sure what to do about this one.
Leave it like it is because it works as intended. It's our default which is also the default for most distributions, and Gentoo users (it's pretty much about them) can still override it by passing -O3 later.
Yeah, ok.
- -pedantic, -Wall, -Wextra, -Wno-format-zero-length, -Wformat-security are all optional and useful for development (including distribution development), and might be security relevant in some cases
They're security relevant in the sense that it's good for us to get the warnings to catch possible security bugs. But a tree that builds clean with the flags shouldn't have different security properties if it's built without them. So I think it only matters that *we* use the flags, we don't need to impose it on anything else.
See above: "(including distribution development)". If those aren't there, we won't get some warnings from building distribution packages, and a few of those were useful in the past.
But, there's an awkward gotcha here. The build.py testcase needs -Werror to catch warnings, but obviously we don't want it to have to reiterate all the other warning flags. If we put these all into CFLAGS, it's tricky for build.py to just add -Werror without touching the rest.
My whole point was / is that CFLAGS needs to *add* to the existing flags, including warnings, instead of replacing them. You can override warnings flags too by appending options.
From our point of view, I like the idea of adding -Werror to the default CFLAGS, which fixes that problem.
At the moment there's no problem though.
But as you pointed out when I did that in the earlier version that might break distro builds causing use more trouble to fix it.
That *would* break distribution builds at the moment, so it's not acceptable. I constantly have fixes for warnings I spot in the packages I build on my to-do list.
Yeah, fair points. -- 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 Tue, 12 May 2026 15:52:44 +1000
David Gibson
While working on pesto, I ran into a number of awkward errors with the static checkers. This series reworks the invocation of the checkers in a way that will let us deal with that. As a bonus, it also gives us static checking for passt-repair. It also a number of other cleanups to the Makefile that seemed natural along the way.
v3: - Rework changes to $(FLAGS) so they're much less likely to have side effects we're not ready for. v2: - Fixed nasty test failure in test/build/build.py
Tested with current packaging rules / build scripts on Alpine, Debian, Fedora, openSUSE, and applied (dropping extra whitespace in 4/12 as noted). I think it would be nice, as a follow-up, to drop the comments that 4/12 introduced: +# Mandatory preprocessor flags that won't be overridden with $(CFLAGS) +# FIXME: Could some of these be default, rather than required? (same for CPPFLAGS), because, as we discussed, those are all default, can actually be overridden as distribution packages already do (so this is misleading for distribution maintainers), and I don't see a particular value in distinguishing what flags *could* be perhaps dropped to have something strictly building. They are all useful for a reason or another. But I didn't touch those, I didn't feel like sneaking in a substantial change like that and I didn't want to delay this series further, either. I had a quick look at package recipes of Chimera, PLD, and Void Linux, I didn't test things there but I don't see any way this series could cause issues there. Somewhat interestingly, I came across many different ways to override flags, taking -D_FORTIFY_SOURCE as example: - Alpine doesn't override / set it at all, the test in our Makefile doesn't set it there, either. Notably, this test was added in commit 38363964fc96 ("Makefile: Enable _FORTIFY_SOURCE iff needed"), specifically for Gentoo - Debian and Void Linux append -D_FORTIFY_SOURCE=2, that is, they duplicate our flag, in CFLAGS - Fedora (with my spec file, where I didn't set any %_fortify_level) overrides it with -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 in CFLAGS - openSUSE used to append -D_FORTIFY_SOURCE=3 (similar to Debian and Void Linux), but now uses -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 - PLD Linux appends -Wp,-D_FORTIFY_SOURCE=2 I found a rather impressive summary here: https://www.anthes.is/nix-internals-cflags.html -- Stefano
On Tue, 12 May 2026 15:52:50 +1000
David Gibson
The $(FLAGS) variable contains mandatory compiler flags that should not be overridden. However, it contains a mixture of flags for the preprocessor and for the compiler proper. That's causing some inconvenience for other Makefile cleanups, so split it into $(BASE_CPPFLAGS) and $(BASE_CFLAGS) variables.
Signed-off-by: David Gibson
--- Makefile | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index a8f8d06e..697f229f 100644 --- a/Makefile +++ b/Makefile @@ -30,12 +30,17 @@ ifeq ($(shell $(CC) -O2 -dM -E - < /dev/null 2>&1 | grep ' _FORTIFY_SOURCE ' > / FORTIFY_FLAG := -D_FORTIFY_SOURCE=2 endif
-FLAGS := -Wall -Wextra -Wno-format-zero-length -Wformat-security -FLAGS += -pedantic -std=c11 -D_XOPEN_SOURCE=700 -D_GNU_SOURCE -FLAGS += $(FORTIFY_FLAG) -O2 -pie -fPIE -FLAGS += -DPAGE_SIZE=$(shell getconf PAGE_SIZE) -FLAGS += -DVERSION=\"$(VERSION)\" -FLAGS += -DDUAL_STACK_SOCKETS=$(DUAL_STACK_SOCKETS) +# Mandatory preprocessor flags that won't be overridden with $(CPPFLAGS) +# FIXME: Could some of these be default, rather than required? +BASE_CPPFLAGS := -D_XOPEN_SOURCE=700 -D_GNU_SOURCE $(FORTIFY_FLAG) +BASE_CPPFLAGS += -DPAGE_SIZE=$(shell getconf PAGE_SIZE) +BASE_CPPFLAGS += -DVERSION=\"$(VERSION)\" +BASE_CPPFLAGS += -DDUAL_STACK_SOCKETS=$(DUAL_STACK_SOCKETS) + +# Mandatory compiler flags that won't be overridden with $(CFLAGS) +# FIXME: Could some of these be default, rather than required? +BASE_CFLAGS := -std=c11 -pie -fPIE -O2 +BASE_CFLAGS += -pedantic -Wall -Wextra -Wno-format-zero-length -Wformat-security
PASST_SRCS = arch.c arp.c bitmap.c checksum.c conf.c dhcp.c dhcpv6.c \ epoll_ctl.c flow.c fwd.c fwd_rule.c icmp.c igmp.c inany.c iov.c ip.c \ @@ -62,11 +67,11 @@ PASST_REPAIR_HEADERS = linux_dep.h
C := \#include
\nint main(){int a=getrandom(0, 0, 0);} ifeq ($(shell printf "$(C)" | $(CC) -S -xc - -o - >/dev/null 2>&1; echo $$?),0) - FLAGS += -DHAS_GETRANDOM + BASE_CPPFLAGS += -DHAS_GETRANDOM endif ifeq ($(shell :|$(CC) -fstack-protector-strong -S -xc - -o - >/dev/null 2>&1; echo $$?),0) - FLAGS += -fstack-protector-strong + BASE_CFLAGS += -fstack-protector-strong endif
prefix ?= /usr/local @@ -89,7 +94,8 @@ endif
all: $(BIN) $(MANPAGES) docs
-static: FLAGS += -static -DGLIBC_NO_STATIC_NSS +static: BASE_CPPFLAGS += -DGLIBC_NO_STATIC_NSS +static: BASE_CFLAGS += -static
Trailing whitespace I fixed up on merge. -- Stefano
On Sat, May 16, 2026 at 05:45:54PM +0200, Stefano Brivio wrote:
On Tue, 12 May 2026 15:52:50 +1000 David Gibson
wrote: The $(FLAGS) variable contains mandatory compiler flags that should not be overridden. However, it contains a mixture of flags for the preprocessor and for the compiler proper. That's causing some inconvenience for other Makefile cleanups, so split it into $(BASE_CPPFLAGS) and $(BASE_CFLAGS) variables.
Signed-off-by: David Gibson
--- Makefile | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index a8f8d06e..697f229f 100644 --- a/Makefile +++ b/Makefile @@ -30,12 +30,17 @@ ifeq ($(shell $(CC) -O2 -dM -E - < /dev/null 2>&1 | grep ' _FORTIFY_SOURCE ' > / FORTIFY_FLAG := -D_FORTIFY_SOURCE=2 endif
-FLAGS := -Wall -Wextra -Wno-format-zero-length -Wformat-security -FLAGS += -pedantic -std=c11 -D_XOPEN_SOURCE=700 -D_GNU_SOURCE -FLAGS += $(FORTIFY_FLAG) -O2 -pie -fPIE -FLAGS += -DPAGE_SIZE=$(shell getconf PAGE_SIZE) -FLAGS += -DVERSION=\"$(VERSION)\" -FLAGS += -DDUAL_STACK_SOCKETS=$(DUAL_STACK_SOCKETS) +# Mandatory preprocessor flags that won't be overridden with $(CPPFLAGS) +# FIXME: Could some of these be default, rather than required? +BASE_CPPFLAGS := -D_XOPEN_SOURCE=700 -D_GNU_SOURCE $(FORTIFY_FLAG) +BASE_CPPFLAGS += -DPAGE_SIZE=$(shell getconf PAGE_SIZE) +BASE_CPPFLAGS += -DVERSION=\"$(VERSION)\" +BASE_CPPFLAGS += -DDUAL_STACK_SOCKETS=$(DUAL_STACK_SOCKETS) + +# Mandatory compiler flags that won't be overridden with $(CFLAGS) +# FIXME: Could some of these be default, rather than required? +BASE_CFLAGS := -std=c11 -pie -fPIE -O2 +BASE_CFLAGS += -pedantic -Wall -Wextra -Wno-format-zero-length -Wformat-security
PASST_SRCS = arch.c arp.c bitmap.c checksum.c conf.c dhcp.c dhcpv6.c \ epoll_ctl.c flow.c fwd.c fwd_rule.c icmp.c igmp.c inany.c iov.c ip.c \ @@ -62,11 +67,11 @@ PASST_REPAIR_HEADERS = linux_dep.h
C := \#include
\nint main(){int a=getrandom(0, 0, 0);} ifeq ($(shell printf "$(C)" | $(CC) -S -xc - -o - >/dev/null 2>&1; echo $$?),0) - FLAGS += -DHAS_GETRANDOM + BASE_CPPFLAGS += -DHAS_GETRANDOM endif ifeq ($(shell :|$(CC) -fstack-protector-strong -S -xc - -o - >/dev/null 2>&1; echo $$?),0) - FLAGS += -fstack-protector-strong + BASE_CFLAGS += -fstack-protector-strong endif
prefix ?= /usr/local @@ -89,7 +94,8 @@ endif
all: $(BIN) $(MANPAGES) docs
-static: FLAGS += -static -DGLIBC_NO_STATIC_NSS +static: BASE_CPPFLAGS += -DGLIBC_NO_STATIC_NSS +static: BASE_CFLAGS += -static
Trailing whitespace I fixed up on merge.
Oops, thanks! -- 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
participants (2)
-
David Gibson
-
Stefano Brivio