Skip to content

Fix GH-13834: Applying non-zero offset 36 to null pointer in zend_jit.c #13846

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

Merged
merged 6 commits into from
Apr 1, 2024

Conversation

nielsdos
Copy link
Member

ssa_op can be NULL in function JIT. Doing pointer arithmetic on a NULL pointer is undefined behaviour. Undefined behaviour can be dangerous because the optimizer may assume then that the variable is not actually NULL.

To solve this:

  1. Add ADVANCE_SSA_OP() to safely add an offset to ssa_op in zend_jit.c
  2. For inference, add an extra offset argument to the helper functions.
  3. Replace open-coded _ssa_op1_info with helper macros because we would have to change those lines anyway for the extra argument.

To reproduce this, use Clang (not GCC) on a test like sapi/cli/tests/gh12363.phpt (or other tests also work).

Targeting master, if no problems we can backport.

…jit.c

ssa_op can be NULL in function JIT. Doing pointer arithmetic on a NULL
pointer is undefined behaviour. Undefined behaviour can be dangerous
because the optimizer may assume then that the variable is not actually
NULL.

To solve this:
1. Add ADVANCE_SSA_OP() to safely add an offset to ssa_op in zend_jit.c
2. For inference, add an extra offset argument to the helper functions.

To reproduce this, use Clang (not GCC) on a test like
sapi/cli/tests/gh12363.phpt (or other tests also work).
@iluuu1994
Copy link
Member

Nice! I'm in favor of fixing this. Can you remove the -fno-sanitize=pointer-overflow flag from .github/workflows/push.yml?

@nielsdos nielsdos requested a review from TimWolla as a code owner March 31, 2024 00:26
@nielsdos
Copy link
Member Author

nielsdos commented Mar 31, 2024

Current remaining failures are all mostly related to the stack map being NULL. I'll give it a go tomorrow.
EDIT: ASAN job now passes, macOS job has a failure but I have seen that one before on the master branch too.

@TimWolla TimWolla removed their request for review March 31, 2024 09:03
static zend_always_inline uint32_t _ssa_##opN##_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op) \
static zend_always_inline uint32_t _ssa_##opN##_info(const zend_op_array *op_array, const zend_ssa *ssa, const zend_op *opline, const zend_ssa_op *ssa_op, size_t offset) \
Copy link
Member

Choose a reason for hiding this comment

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

I would prefer not to change the "public" API prototypes.
Let me think a bit...

Copy link
Member

@dstogov dstogov left a comment

Choose a reason for hiding this comment

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

Please check my comments. The changes to zend_jit_trace.c also look unrelated.
This probably should be targeted to PHP-8.2

Comment on lines -200 to -201
#define OP1_DATA_INFO() (_ssa_op1_info(op_array, ssa, (opline+1), (ssa_op+1)))
#define OP2_DATA_INFO() (_ssa_op2_info(op_array, ssa, (opline+1), (ssa_op+1)))
Copy link
Member

Choose a reason for hiding this comment

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

Can you change this into

-#define OP1_DATA_INFO()      (_ssa_op1_info(op_array, ssa, (opline+1), (ssa_op+1)))
-#define OP2_DATA_INFO()      (_ssa_op2_info(op_array, ssa, (opline+1), (ssa_op+1)))
+#define OP1_DATA_INFO()      (_ssa_op1_info(op_array, ssa, (opline+1), ssa_op ? (ssa_op+1) : NULL))
+#define OP2_DATA_INFO()      (_ssa_op2_info(op_array, ssa, (opline+1), ssa_op ? (ssa_op+1) : NULL))

I think this should lead to a simple diff.

Copy link
Member Author

Choose a reason for hiding this comment

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

OK, done.

zend_jit_trace_stack *parent_stack =
zend_jit_trace_stack *parent_stack = parent_vars_count == 0 ? NULL :
Copy link
Member

Choose a reason for hiding this comment

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

What do you fix by this? (this is not related).

Copy link
Member Author

Choose a reason for hiding this comment

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

After removing -fno-sanitize=pointer-overflow from CI, more pointer arithmetic on NULL was found. This was one of the places that the sanitizer complained about. parent->stack_map may be NULL when parent_vars_count == 0, so I added this check to fix that.

@nielsdos nielsdos merged commit 00c6d53 into php:master Apr 1, 2024
@dstogov
Copy link
Member

dstogov commented Apr 1, 2024

Hi @nielsdos, it looks like this patch made "slowdown" on bench.php with tracing JIT. see https://nielsdos.github.io/php-benchmark-visualisation/
Checking the recent changes I see a difference in type inference in traces forsieve() function.

 ---- TRACE 61 stop (loop)
 ---- TRACE 61 TSSA start (loop) sieve() /home/dmitry/php/php-master/Zend/bench.php:325
-     ;#2.CV2($flags) [rc1, [packed, hash] array [long] of [long]]
+     ;#2.CV2($flags) [rc1, [packed, hash] array [long] of [long, double, string]]
      ;#3.CV3($i) [long] RANGE[2..8192]
      ;#4.CV4($k) [long] RANGE[4..16384]
 LOOP:
-     ;#8.CV2($flags) [rc1, [packed, hash] array [long] of [long]] = Phi(#2.CV2($flags) [rc1, [packed, hash] array [long] of [long]], #11.CV2($flags) [rc1, [p
acked, hash] array [long] of [long]])
+     ;#8.CV2($flags) [rc1, [packed, hash] array [long] of [long, double, string]] = Phi(#2.CV2($flags) [rc1, [packed, hash] array [long] of [long, double, st
ring]], #11.CV2($flags) [rc1, [packed, hash] array [long] of [long, double, string]])
      ;#9.CV4($k) [long] RANGE[4..16384] = Phi(#4.CV4($k) [long] RANGE[4..16384], #12.CV4($k) [long] RANGE[6..16384])
 0019 #10.T5 [bool] = IS_SMALLER_OR_EQUAL #9.CV4($k) [long] RANGE[4..16384] int(8192) ; op1(int)
 0020 ;JMPNZ #10.T5 [bool] 0016
-0016 ASSIGN_DIM #8.CV2($flags) [rc1, [packed, hash] array [long] of [long]] -> #11.CV2($flags) [rc1, [packed, hash] array [long] of [long]] #9.CV4($k) [long]
 RANGE[4..16384] ; op1(packed array) op2(int) val(int)
+0016 ASSIGN_DIM #8.CV2($flags) [rc1, [packed, hash] array [long] of [long, double, string]] -> #11.CV2($flags) [rc1, [packed, hash] array [long] of [long, do
uble, string]] #9.CV4($k) [long] RANGE[4..16384] ; op1(packed array) op2(int) val(int)
 0017 ;OP_DATA int(0)
 0018 ADD #9.CV4($k) [long] RANGE[4..16384] #3.CV3($i) [long] RANGE[2..8192] #9.CV4($k) [long] RANGE[4..16384] -> #12.CV4($k) [long] RANGE[6..16384] ; op1(int
) op2(int)
 ---- TRACE 61 TSSA stop (loop)

This later causes generation of more expensive code.

I suspect, the problem is in changes of type inference for range().
Could you please analyse this?

@nielsdos
Copy link
Member Author

nielsdos commented Apr 1, 2024

I suspect, the problem is in changes of type inference for range().
Could you please analyse this?

Yes, I have also noticed this and analysed this already. I had committed eb1cdb5 to fix this.

@dstogov
Copy link
Member

dstogov commented Apr 1, 2024

I had committed eb1cdb5 to fix this.

Thanks. This seems fixed the problem.

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.

Applying non-zero offset 36 to null pointer in zend_jit.c
3 participants