Skip to content

Fixed bug #8338 (Intel CET is disabled unintentionally since PHP-8.1.0) #8339

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

chen-hu-97
Copy link
Contributor

Intel Control Flow Enforcement Technology (CET) is enabled by default
since gcc v8.1. With CET, gcc emits following processor-specific program
property types in .note.gnu.property section:

  • Indirect Branch Tracking (IBT)
  • Shadow Stack (SHSTK)

Such properties are there before PHP-8.1.0.
$ sudo readelf -n sapi/cli/php
Displaying notes found in: .note.gnu.property
Owner Data size Description
GNU 0x00000020 NT_GNU_PROPERTY_TYPE_0
Properties: x86 feature: IBT, SHSTK
x86 ISA needed: x86-64-baseline

However, the properties are missing since PHP-8.1.0.
$ sudo readelf -n sapi/cli/php
Displaying notes found in: .note.gnu.property
Owner Data size Description
GNU 0x00000010 NT_GNU_PROPERTY_TYPE_0
Properties: x86 ISA needed: x86-64-baseline

This is caused by commit c276c16 "Implement Fibers" which introduces
some assembly files such as jump_x86_64_sysv_elf_gas.S and
make_x86_64_sysv_elf_gas.S. The object files of such assembly miss
.note.gnu.property section and thus the final output files miss it too.

Fix this via adding linker option.

Signed-off-by: Chen, Hu hu1.chen@intel.com

@cmb69
Copy link
Member

cmb69 commented Apr 11, 2022

cc @trowski

@trowski
Copy link
Member

trowski commented Apr 12, 2022

I know nothing about Intel CET, but at a glance this seems to make sense.

@@ -141,7 +141,7 @@ typedef struct {

/* These functions are defined in assembler files provided by boost.context (located in "Zend/asm"). */
extern void *make_fcontext(void *sp, size_t size, void (*fn)(boost_context_data));
extern boost_context_data jump_fcontext(void *to, zend_fiber_transfer *transfer);
extern boost_context_data jump_fcontext(void *to, zend_fiber_transfer *transfer) __attribute__ ((__indirect_return__));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__attribute__ ((__indirect_return__)) is not necessarily supported (see the failing CI builds), and as such must not be applied uncoditionally.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

attribute ((indirect_return)) is relatively new gcc attreibute. So I need figure out other fix.
How about insert one line inline assembly after jump_fcontext(). Maybe that's too ugly?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it is okay to only enable shstk if __attribute__((indirect_return)) (or equivalent) is supported by the compiler. Those interested in best security practices would need to use a newish compiler version, but that may be okay.

I wouldn't know if something like __attribute__((indirect_return)) is supported by MSVC. CET support is generally available there.

Copy link
Contributor Author

@chen-hu-97 chen-hu-97 Apr 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on my current understanding (maybe it's not correct), there are no such attribute for shadow stack.
Basically, the compiler such as gcc take care of shadow stack. No special treatment is needed for application.

But Fiber or applications use makecontext() are exception. They make context (i.e., they maintain stack) by themselves instead of gcc/libc. So they also need creating a shadow stack which requires kernel support. Such kernel patch is under review.

@@ -30,6 +30,7 @@
.type jump_fcontext,@function
.align 16
jump_fcontext:
endbr64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm not mistaken, these assembler files are re-used from the boost libraries, and as such it would probably be a good idea to provide these modifications upstream.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this file is imported from boostorg/context, so please do provide any changes here upstream as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I will try to provide CET support to boostorg/context as well and then echo here.

@chen-hu-97
Copy link
Contributor Author

chen-hu-97 commented Apr 26, 2022

I know nothing about Intel CET, but at a glance this seems to make sense.

Hi @trowski,
Intel CET is a HW security feature in some Intel and AMD CPUs, for example, since Intel TigerLake and Sapphire Rapids. It includes:

  • Indirect Branch Tracking (IBT)
    Any indirect CALL/JMP must target an ENDBR instruction or suffer Control Protection (CP)

  • Shadow Stack (SHSTK)
    When func return, it pops out the return address from stack and shadow stack. If the addresses are different, contol protection is raised.

Below supports are needed to enable CET:

  1. gcc/libc/linker already support this since gcc v8.1
  2. Kernel.
    Kernel support for kernel IBT has been merged in 5.18 RC2
    Kernel support for user space IBT is still on the way of upstream. (Php is in this category)
    Kernel support for SHSTK is under review.

@hjl-tools has a nice explanation for CET in LPC. https://lpc.events/event/2/contributions/147/attachments/72/83/CET-LPC-2018.pdf

@chen-hu-97
Copy link
Contributor Author

Thanks community for the detailed review. Now the reviews focus on Fiber part support for "indirect branch tracking".
For JIT part, I add new instructions to Dynasm https://luajit.org/git/luajit.git. Maybe it would probably be a good idea to provide these modifications upstream as well?

@devnexen
Copy link
Member

Would it be better to do separate PR at this point tough ?

@mvorisek
Copy link
Contributor

mvorisek commented May 4, 2022

can this target PHP 8.1 and is the Intel CET security feature supported also with MSVC?

@cmb69
Copy link
Member

cmb69 commented May 4, 2022

[…] is the Intel CET security feature supported also with MSVC?

See https://docs.microsoft.com/en-us/cpp/build/reference/cetcompat?view=msvc-170.

@mvorisek
Copy link
Contributor

mvorisek commented May 4, 2022

[…] is the Intel CET security feature supported also with MSVC?

See https://docs.microsoft.com/en-us/cpp/build/reference/cetcompat?view=msvc-170.

added to #8392 to see if tests pass

can the compiler default flags be specified somewhere so pure ./configure.bat & nmake will have them set by default?

@cmb69
Copy link
Member

cmb69 commented May 4, 2022

/CETCOMPAT needs to be added to buildconf.js/confutils.js (and is a linker flag, by the way).

@cmb69
Copy link
Member

cmb69 commented May 4, 2022

The following patch should do for VS 2019 and up:

 win32/build/confutils.js | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/win32/build/confutils.js b/win32/build/confutils.js
index 9bf1bf2814..1571d1964f 100644
--- a/win32/build/confutils.js
+++ b/win32/build/confutils.js
@@ -3401,10 +3401,13 @@ function toolset_setup_common_ldlags()
 	ADD_FLAG("PHP_LDFLAGS", "/nodefaultlib:libcmt");
 
 	if (VS_TOOLSET) {
-		if (VCVERS >= 1900) {
-			if (PHP_SECURITY_FLAGS == "yes") {
+		if (PHP_SECURITY_FLAGS == "yes") {
+			if (VCVERS >= 1900) {
 				ADD_FLAG('LDFLAGS', "/GUARD:CF");
 			}
+			if (VCVERS >= 1920) {
+				ADD_FLAG('LDFLAGS', "/CETCOMPAT");
+			}
 		}
 		if (PHP_VS_LINK_COMPAT != "no") {
 			// Allow compatible IL versions, do not require an exact match.

@mvorisek
Copy link
Contributor

mvorisek commented May 4, 2022

this PR should target PHP 8.1 (or maybe 8.0, but the desc claims the support is broken since PHP 8.1)

@chen-hu-97
Copy link
Contributor Author

this PR should target PHP 8.1 (or maybe 8.0, but the desc claims the support is broken since PHP 8.1)

I thinks it targets PHP8.1.

If the user want to compile the PHP to enable CET, he should specify gcc "-fcf-protection" option explicitly. Below is an example:
$ ./buildconf --force && CC="gcc -fcf-protection" ./configure && make -j64

However, PHP 8.1 introduces some hand-coded assemblers (from Fiber) which ignores/breaks this gcc option. With this PR, if "-fcf-protection" is detected, linker options will be added to make sure the final PHP bin is CET enabled.

@chen-hu-97
Copy link
Contributor Author

Per @devnexen's comments, I separate the patches. Now this PR focus on Fiber part. It's based on merged PR in boost: boostorg/context#199.

This patch passes test on a full CET environment/stack at a CET enabled Intel CPU. Without the patch, fiber test will fail because IBT violation.

Component Upstreamed?
gcc Y
glibc Y
Linux Kernel: IBT support for userspace N

$ make test TESTS="Zend/tests/fibers"
=====================================================================
Number of tests : 60 60
Tests skipped : 0 ( 0.0%) --------
Tests warned : 0 ( 0.0%) ( 0.0%)
Tests failed : 0 ( 0.0%) ( 0.0%)
Tests passed : 60 (100.0%) (100.0%)
---------------------------------------------------------------------
Time taken : 1 seconds
=====================================================================

@chen-hu-97 chen-hu-97 requested review from trowski and cmb69 May 19, 2022 01:16
Copy link
Member

@trowski trowski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than the minor nit about attribute placement, this LGTM.

@ramsey
Copy link
Member

ramsey commented May 23, 2022

If this is a bug in PHP 8.1, please change the base branch to PHP-8.1. Thanks!

@chen-hu-97
Copy link
Contributor Author

If this is a bug in PHP 8.1, please change the base branch to PHP-8.1. Thanks!

Hi @ramsey, I saw this issue in fiber since PHP 8.1. It's also in current master. For such case, shall we change base branch to 8.1?

@ramsey
Copy link
Member

ramsey commented May 24, 2022

Current master is 8.2.0-dev, so if it exists in both PHP-8.1 and master, then you need to target PHP-8.1 and we will merge it up into master.

If the bug also exists in PHP-8.0, then you should target PHP-8.0 instead.

@cmb69
Copy link
Member

cmb69 commented May 24, 2022

Just to be sure, adding __attribute__((__indirect_return__)) wouldn't change the function's ABI, or would it?

@chen-hu-97
Copy link
Contributor Author

Just to be sure, adding __attribute__((__indirect_return__)) wouldn't change the function's ABI, or would it?

"No, it won't change ABI."
I got feedback from the author of the indirect_return in gcc.

@chen-hu-97 chen-hu-97 changed the base branch from master to PHP-8.1 May 25, 2022 04:12
Indirect Branch Tracking (IBT) is part of Intel's Control-Flow
Enforcement Technology (CET). IBT is hardware based, forward edge
Control-Flow-Integrity mechanism where any indirect CALL/JMP must target
an ENDBR instruction or suffer #CP.

This commit adds IBT support for fiber:
1. Add endbr32/64 in assembly
2. Inform compiler jump_fcontext may return via indirect branch

Furthermore:
gcc support CET since v8.1 and set it to default since gcc 11. That is,
the ELF header of sapi/cli/php has a property named IBT. However, such
property is lost since PHP8.1 because the assembly introduced by Fiber.
This commit also fixes this.

Signed-off-by: Chen, Hu <hu1.chen@intel.com>
Co-authored-by: Christoph M. Becker <cmbecker69@gmx.de>
@chen-hu-97
Copy link
Contributor Author

chen-hu-97 commented May 25, 2022

Current master is 8.2.0-dev, so if it exists in both PHP-8.1 and master, then you need to target PHP-8.1 and we will merge it up into master.

If the bug also exists in PHP-8.0, then you should target PHP-8.0 instead.

Hi @ramsey,
I change the target branch to PHP-8.1. But the from branch is master and thus looks like there are some conflicts. Maybe I need create a new PR whose from branch is also PHP-8.1?

I am new on the flow of github. Sorry for the inconvenience.

@cmb69
Copy link
Member

cmb69 commented May 25, 2022

I change the target branch to PHP-8.1.

The proper way to do this for php-src is to first rebase the branch onto the new target branch (PHP-8.1), then force-push, and only then change the target branch via UI.

Copy link
Member

@cmb69 cmb69 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@ramsey ramsey linked an issue May 25, 2022 that may be closed by this pull request
@ramsey ramsey closed this in 040a37d May 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Intel CET is disabled unintentionally since PHP-8.1.0
6 participants