Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 807766 - net-libs/libnftnl-1.2.0 wrong snprintf substitution with clang-cpp and -D_FORTIFY_SOURCE
Summary: net-libs/libnftnl-1.2.0 wrong snprintf substitution with clang-cpp and -D_FOR...
Status: RESOLVED FIXED
Alias: None
Product: Gentoo Linux
Classification: Unclassified
Component: Current packages (show other bugs)
Hardware: All Linux
: Normal normal (vote)
Assignee: Gentoo's Team for Core System packages
URL:
Whiteboard:
Keywords: PATCH
Depends on:
Blocks: systemwide-clang fortify-source
  Show dependency tree
 
Reported: 2021-08-11 21:26 UTC by Jannik Glückert
Modified: 2022-08-16 00:04 UTC (History)
4 users (show)

See Also:
Package list:
Runtime testing required: ---


Attachments
fix building on clang (snprintf.patch,1.00 KB, patch)
2021-08-11 21:26 UTC, Jannik Glückert
Details | Diff
alternate_clang.patch (alternate_clang.patch,22.61 KB, patch)
2021-12-01 15:50 UTC, nvinson234
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Jannik Glückert 2021-08-11 21:26:49 UTC
Created attachment 732337 [details, diff]
fix building on clang

This one's certainly unordinary:

trying to build libnftnl-1.2.0 with clang-12 and -D_FORTIFY_SOURCE=2 fails with the following:

> expr.c:285:19: error: no member named '__builtin___snprintf_chk' in 'struct expr_ops'
> ret = expr->ops->snprintf(buf + offset, remain, flags, expr);

whereas it succeeds with gcc. On closer investigation, clang-cpp does indeed replace the snprintf in the struct, whereas gcc-cpp does not - I have no idea which one is in the wrong here though

Steps to reproduce:

ebuild $(equery which libnftnl) clean configure (encountered this with 1.2.0, probably happens with other versions too)
cd into work/libnftnl-1.2.0
look at the output of
clang-cpp -D_FORTIFY_SOURCE=2 -Iinclude -I. -O1 src/expr.c | grep __builtin___snprintf_chk
vs
cpp -D_FORTIFY_SOURCE=2 -Iinclude -I. -O1 src/expr.c | grep __builtin___snprintf_chk

attached is a patch that pushes / pops the snprintf definition for the offending lines of code, this fixes building with clang.
Comment 1 Sergei Trofimovich 2021-08-11 22:20:26 UTC
Probably a good enough reproducer to illustrate the divergence:

$ cat expr.c
#include <stdio.h>

struct expr_ops {
  int (*snprintf)(char *buf, size_t len, const char * fmt, ...);
};


void f(const struct expr_ops *ops)
{
  ops->snprintf(NULL, 0, 0, NULL);
}
$ gcc -D_FORTIFY_SOURCE=2 -O2 -c expr.c -o expr.o; echo "gcc: $?"; clang -D_FORTIFY_SOURCE=2 -O2 -c expr.c -o expr.o; echo "clang: $?"

gcc: 0

expr.c:10:8: error: no member named '__builtin___snprintf_chk' in 'struct expr_ops'
  ops->snprintf(NULL, 0, 0, NULL);
  ~~~  ^
/usr/include/bits/stdio2.h:77:3: note: expanded from macro 'snprintf'
  __builtin___snprintf_chk (str, len, __USE_FORTIFY_LEVEL - 1,                \
  ^
1 error generated.
clang: 1

gcc probably treats snprintf as a special identifier:

gcc/builtins.def:DEF_C99_BUILTIN        (BUILT_IN_SNPRINTF, "snprintf", BT_FN_INT_STRING_SIZE_CONST_STRING_VAR, ATTR_FORMAT_PRINTF_NOTHROW_3_4)
Comment 2 nvinson234 2021-12-01 15:45:36 UTC
The root cause is glibc's /usr/include/bits/stdio2.h header file.

If __va_arg_pack is not defined and __cplusplus is not defined, glibc defines sprintf as the macro:

# define sprintf(str, ...) __builtin___sprintf_chk (str, __USE_FORTIFY_LEVEL - 1, __glibc_objsize (str), __VA_ARGS__)

and it's this macro definition that triggers the bug in clang. By adding the following two lines to  Sergei Trofimovich's example the error can be triggered with both gcc and clang:

    #include<features.h>
    #undef __va_arg_pack

$ cat expr.c
#include<features.h>
#undef __va_arg_pack
#include <stdio.h>

struct expr_ops {
  int (*snprintf)(char *buf, size_t len, const char * fmt, ...);
};


void f(const struct expr_ops *ops)
{
  ops->snprintf(NULL, 0, 0, NULL);
}
$ gcc -D_FORTIFY_SOURCE=2 -O2 -c expr.c -o expr.o; echo "gcc: $?"; clang -D_FORTIFY_SOURCE=2 -O2 -c expr.c -o expr.o; echo "clang: $?"
expr.c: In function ‘f’:
expr.c:12:6: error: ‘const struct expr_ops’ has no member named ‘__builtin___snprintf_chk’
   12 |   ops->snprintf(NULL, 0, 0, NULL);
      |      ^~
gcc: 1
expr.c:12:8: error: no member named '__builtin___snprintf_chk' in 'struct expr_ops'
  ops->snprintf(NULL, 0, 0, NULL);
  ~~~  ^
/usr/include/bits/stdio2.h:77:3: note: expanded from macro 'snprintf'
  __builtin___snprintf_chk (str, len, __USE_FORTIFY_LEVEL - 1,                \
  ^
1 error generated.
clang: 1
Comment 3 nvinson234 2021-12-01 15:50:52 UTC
Created attachment 757132 [details, diff]
alternate_clang.patch

This is an alternate patch to fix clang building errors.  Overall, it is a larger patch than the one previously submitted, but fixes the issue without relying on compiler-specific Pragmas.
Comment 5 nvinson234 2022-06-15 11:41:11 UTC
Sent to upstream mailing list. Subject line is `build: fix clang+glibc snprintf substitution error`
Comment 6 Sam James archtester Gentoo Infrastructure gentoo-dev Security 2022-06-15 11:49:56 UTC
(In reply to nvinson234 from comment #5)
> Sent to upstream mailing list. Subject line is `build: fix clang+glibc
> snprintf substitution error`

https://marc.info/?l=netfilter-devel&m=165529290515437&w=2
Comment 7 nvinson234 2022-06-15 13:03:17 UTC
Fixed upstream

commit 84d12cfacf8ddd857a09435f3d982ab6250d250c (HEAD -> master, origin/master, origin/HEAD)
Author: Nicholas Vinson <nvinson234@gmail.com>
Date:   Wed Jun 15 07:35:28 2022 -0400

    build: fix clang+glibc snprintf substitution error
    
    When building with clang and glibc and -D_FORTIFY_SOURCE=2 is passed to
    clang, the snprintf member of the expr_ops and obj_ops structures will
    be incorrectly replaced with __builtin_snprintf_chk() which results in
    "error: no member named '__builtin___snprintf_chk'" errors at build
    time.
    
    This patch changes the member name from 'snprintf' to 'output' to
    prevent the replacement.
    
    This bug can be emulated using GCC by undefining the __va_arg_pack macro
    before stdio.h is included.
    
    This patch is based on the notes provided in
    https://bugs.gentoo.org/807766.
    
    Signed-off-by: Nicholas Vinson <nvinson234@gmail.com>
    Signed-off-by: Florian Westphal <fw@strlen.de>
Comment 8 Larry the Git Cow gentoo-dev 2022-06-15 13:12:03 UTC
The bug has been closed via the following commit(s):

https://gitweb.gentoo.org/repo/gentoo.git/commit/?id=6d5b60ff3245104e71b1cc8c055105454824bf13

commit 6d5b60ff3245104e71b1cc8c055105454824bf13
Author:     Sam James <sam@gentoo.org>
AuthorDate: 2022-06-15 13:11:45 +0000
Commit:     Sam James <sam@gentoo.org>
CommitDate: 2022-06-15 13:11:51 +0000

    net-libs/libnftnl: fix build w/ Clang and FORTIFY_SOURCE=2
    
    Thanks-to: Nicholas Vinson <nvinson234@gmail.com>
    Closes: https://bugs.gentoo.org/807766
    Signed-off-by: Sam James <sam@gentoo.org>

 net-libs/libnftnl/Manifest                 |  1 +
 net-libs/libnftnl/libnftnl-1.2.2-r1.ebuild | 87 ++++++++++++++++++++++++++++++
 2 files changed, 88 insertions(+)