oVirt/ovirt-imageio

Consider enabling cf-protection

nirs opened this issue · 7 comments

nirs commented

rpmdiff complains about missing cf-protection:

Detecting usr/lib64/python3.6/site-packages/ovirt_imageio/_internal/ioutil.cpython-36m-x86_64-linux-gnu.so with not-hardened warnings '
Hardened: ioutil.cpython-36m-x86_64-linux-gnu.so: WARN: The annobin plugin was built to run on a newer version of the compiler
Hardened: ioutil.cpython-36m-x86_64-linux-gnu.so: FAIL: cf-protection test because no protection enabled 
Hardened: Rerun annocheck with --verbose to see more information on the tests.
' on x86_64

Check what is this cf-protection and how to enable it for next build.

@sandrobonazzola do you have more info about this issue?

Seems like you're missing -fcf-protection=full (x86 and x86_64 only) flag in GCC compiler options.

As of RHEL 8, all programs compiled by gcc and llvm should include the following options on the command line:

  • -D_FORTIFY_SOURCE=2
  • -D_GLIBCXX_ASSERTIONS
  • -fstack-protector-strong
  • -fexceptions
  • -O2 (or higher)
  • -fcf-protection=full (x86 and x86_64 only)
  • -mstackrealign (x86 only)
  • -fPIC (for libraries) or -fPIE (for executables)
  • -fstack-clash-protection
  • -Wl,-z,now

redhat-rpm-config packages the required flags up into RPM_OPT_FLAGS and RPM_LD_FLAGS.

So perhaps you're missing redhat-rpm-config in imageio dependency tree?

nirs commented

I see that @oliel handled the same issue by adding extra compiler arg:
https://github.com/oVirt/python-ovirt-engine-sdk4/pull/62/files

@sandrobonazzola do you mean to add "BuildRequires: redhat-rpm-config"?

We use %py3_build - I think this should be handled by the macro, and not
required all package maintainers to do extra work, which is error prone.

Yes, the buildrequires should solve. I can only guess that the %py3_build macro has been kept generic allowing to differentiate flag sets between different distributions.

nirs commented

I looked at the build logs in brew:

Brew root log:
http://{host}/brewroot/work/tasks/5592/43785592/root.log

DEBUG util.py:636: redhat-rpm-config noarch 125-1.el8 build 85 k

Brew build log:
http://{host}/brewroot/work/tasks/5592/43785592/build.log

gcc -pthread -DDYNAMIC_ANNOTATIONS_ENABLED=1 -DNDEBUG -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -D_GNU_SOURCE -fPIC -fwrapv -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fPIC -I/usr/include/python3.6m -c ovirt_imageio/_internal/ioutil.c -o build/temp.linux-x86_64-3.6/ovirt_imageio/_internal/ioutil.o

gcc -pthread -shared -Wl,-z,relro -Wl,-z,now -g -Wl,-z,relro -Wl,-z,now -g -Wl,-z,relro -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection build/temp.linux-x86_64-3.6/ovirt_imageio/_internal/ioutil.o -L/usr/lib64 -lpython3.6m -o build/lib.linux-x86_64-3.6/ovirt_imageio/_internal/ioutil.cpython-36m-x86_64-linux-gnu.so

And -fcf-protection is used.

According to
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html-single/developing_c_and_cpp_applications_in_rhel_8/index#hardening-checker-options_hardening-checker-basics

We should have:

If available, the -fcf-protection=full option was used.

Looking in redhat-rpm-config files in Fedora 35, we have:

$ grep -rn -- '-fcf-protection' /usr/lib/rpm/
/usr/lib/rpm/redhat/rpmrc:6:optflags: i686 %{__global_compiler_flags} -m32 -march=i686 -mtune=generic -msse2 -mfpmath=sse -mstackrealign -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection
/usr/lib/rpm/redhat/rpmrc:9:optflags: x86_64 %{__global_compiler_flags} -m64 %{__cflags_arch_x86_64} -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection

$ dnf whatprovides /usr/lib/rpm/redhat/rpmrc
Last metadata expiration check: 0:35:32 ago on Tue 15 Mar 2022 04:05:11 AM IST.
redhat-rpm-config-197-1.fc35.noarch : Red Hat specific rpm configuration files
Repo        : fedora
Matched from:
Filename    : /usr/lib/rpm/redhat/rpmrc

So this looks like an issue with redhat-rpm-config, using -fcf-protection instead of -fcf-proection=full.

gcc(1) (on Fedora 35) document several settings (full, branch, return, none, check)
but does not specify what is the default if no value is used.

       -fcf-protection=[full|branch|return|none|check]
           Enable code instrumentation of control-flow transfers to increase
           program security by checking that target addresses of control-flow
           transfer instructions (such as indirect function call, function
           return, indirect jump) are valid.  This prevents diverting the flow
           of control to an unexpected target.  This is intended to protect
           against such threats as Return-oriented Programming (ROP), and
           similarly call/jmp-oriented programming (COP/JOP).

           The value "branch" tells the compiler to implement checking of
           validity of control-flow transfer at the point of indirect branch
           instructions, i.e. call/jmp instructions.  The value "return"
           implements checking of validity at the point of returning from a
           function.  The value "full" is an alias for specifying both
           "branch" and "return". The value "none" turns off instrumentation.

           The value "check" is used for the final link with link-time
           optimization (LTO).  An error is issued if LTO object files are
           compiled with different -fcf-protection values.  The value "check"
           is ignored at the compile time.

           The macro "__CET__" is defined when -fcf-protection is used.  The
           first bit of "__CET__" is set to 1 for the value "branch" and the
           second bit of "__CET__" is set to 1 for the "return".

           You can also use the "nocf_check" attribute to identify which
           functions and calls should be skipped from instrumentation.

           Currently the x86 GNU/Linux target provides an implementation based
           on Intel Control-flow Enforcement Technology (CET) which works for
           i686 processor or newer.

Testing on Fedora show:

$ make
...
gcc -Wno-unused-result -Wsign-compare -DDYNAMIC_ANNOTATIONS_ENABLED=1 -DNDEBUG -O2 -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -D_GNU_SOURCE -fPIC -fwrapv -O2 -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -D_GNU_SOURCE -fPIC -fwrapv -O2 -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -D_GNU_SOURCE -fPIC -fwrapv -fPIC -I/usr/include/python3.10 -c ovirt_imageio/_internal/ioutil.c -o build/temp.linux-x86_64-3.10/ovirt_imageio/_internal/ioutil.o

gcc -shared -Wl,-z,relro -Wl,--as-needed -Wl,-z,now -g -Wl,-z,relro -Wl,--as-needed -Wl,-z,now -g build/temp.linux-x86_64-3.10/ovirt_imageio/_internal/ioutil.o -L/usr/lib64 -o /home/nsoffer/src/ovirt-imageio/ovirt_imageio/_internal/ioutil.cpython-310-x86_64-linux-gnu.so

Checking the built executable show:

$ annocheck -v ovirt_imageio/_internal/ioutil.cpython-310-x86_64-linux-gnu.so | grep cf-protection
Hardened: ovirt_imageio/_internal/ioutil.cpython-310-x86_64-linux-gnu.so: PASS: cf-protection test 

Testing make rpm:

$ make rpm
...
gcc -Wno-unused-result -Wsign-compare -DDYNAMIC_ANNOTATIONS_ENABLED=1 -DNDEBUG -O2 -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -D_GNU_SOURCE -fPIC -fwrapv -O2 -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -D_GNU_SOURCE -fPIC -fwrapv -O2 -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fstack-protector-strong -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -D_GNU_SOURCE -fPIC -fwrapv -O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fPIC -I/usr/include/python3.10 -c ovirt_imageio/_internal/ioutil.c -o build/temp.linux-x86_64-3.10/ovirt_imageio/_internal/ioutil.o

gcc -shared -Wl,-z,relro -Wl,--as-needed -Wl,-z,now -g -Wl,-z,relro -Wl,--as-needed -Wl,-z,now -g -Wl,-z,relro -Wl,--as-needed -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection build/temp.linux-x86_64-3.10/ovirt_imageio/_internal/ioutil.o -L/usr/lib64 -o build/lib.linux-x86_64-3.10/ovirt_imageio/_internal/ioutil.cpython-310-x86_64-linux-gnu.so

Testing the built rpm show:

$ annocheck -v dist/ovirt-imageio-common-2.4.2-0.202203151143.git089d54b.fc35.x86_64.rpm | grep cf-protection
Hardened: ./usr/lib64/python3.10/site-packages/ovirt_imageio/_internal/ioutil.cpython-310-x86_64-linux-gnu.so: PASS: cf-protection test 

Looks correct.

So I think we have several issues:

  • gcc: need to document the semantics of bare -fcf-protection
  • redhat-rpm-config: maybe should use -fcf-protection=full
  • rpmdiff: maybe need to handle better different versions of gcc/annobin

But I don't see any issue with imageio spec.

@nirs can you please report a BZ for:

So this looks like an issue with redhat-rpm-config, using -fcf-protection instead of -fcf-proection=full.

nirs commented
nirs commented

So turns out that redhat-rpm-config is correct when using

-fcf-protection

since it is an alias for

-fcf-protection=full

The real issue is mismatch between gcc and annobin plugin, causing a bogus failure in annocheck.

Closing since there is nothing to do on our side.