php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #81225 Wrong result with pow operator with JIT enabled
Submitted: 2021-07-05 23:31 UTC Modified: 2021-07-06 12:48 UTC
From: smokey101stair at gmail dot com Assigned: dmitry (profile)
Status: Closed Package: JIT
PHP Version: 8.0.8 OS: Ubuntu 20.04
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: smokey101stair at gmail dot com
New email:
PHP Version: OS:

 

 [2021-07-05 23:31 UTC] smokey101stair at gmail dot com
Description:
------------
When the JIT is enabled, the test script sometimes returns -4294967294 instead of 2. This seems to be related to the ** operator as it appears to work fine if replaced with the pow() function.

I get the following with opcache.jit_debug=1

TRACE-1$/home/trevor/jit_test.php$16: ; (unknown)
        mov $EG(jit_trace_num), %rax
        mov $0x1, (%rax)
        mov $EG(vm_stack_end), %rax
        mov (%rax), %rcx
        mov $EG(vm_stack_top), %rax
        sub (%rax), %rcx
        cmp $0xf0, %rcx
        jb jit$$trace_exit_0
.L1:
        cmp $0x4, 0x68(%r14)
        jnz jit$$trace_exit_1
        cmp $0xc8, 0x60(%r14)
        jge jit$$trace_exit_2
        add $0xfffffffffffffec0, %r15
        mov $0x5633ad4d2080, %rax
        call *%rax
        mov $EG(exception), %rcx
        cmp $0x0, (%rcx)
        jnz JIT$$exception_handler
        cmp $0x41675ab8, %r15d
        jnz jit$$trace_exit_3
        mov $EG(vm_stack_top), %r15
        mov (%r15), %r15
        mov $EG(vm_stack_top), %rdx
        add $0x70, (%rdx)
        mov $0x0, 0x28(%r15)
        mov $0x5633af37bbf0, %rcx
        mov %rcx, 0x18(%r15)
        mov $0x0, 0x20(%r15)
        mov $0x2, 0x2c(%r15)
        mov $0x0, 0x30(%r15)
        mov %r15, 0x8(%r14)
        mov 0x40(%r14), %rax
        mov 0x10(%rax), %rax
        test %rax, %rax
        jz .L7
.L2:
        mov $EG(vm_stack_top), %r15
        mov (%r15), %r15
        mov $EG(vm_stack_top), %rdx
        add $0x80, (%rdx)
        mov $0x0, 0x28(%r15)
        mov %rax, 0x18(%r15)
        mov $0x0, 0x20(%r15)
        mov $0x1, 0x2c(%r15)
        cmp $0x4, 0x58(%r14)
        jnz jit$$trace_exit_4
        mov 0x50(%r14), %rdx
        mov %rdx, 0x50(%r15)
        mov $0x4, 0x58(%r15)
        mov $0x41675b18, (%r14)
        mov %r14, 0x30(%r15)
        mov $0x0, 0x8(%r15)
        lea 0x80(%r14), %rdx
        mov %rdx, 0x10(%r15)
        mov 0x18(%r15), %rax
        mov 0x50(%rax), %rdx
        mov $0x5633ae520f78, %rcx
        add (%rcx), %rdx
        mov (%rdx), %rdx
        mov %rdx, 0x40(%r15)
        mov $EG(current_execute_data), %rcx
        mov %r15, (%rcx)
        mov %r15, %r14
        mov $0x0, 0x68(%r15)
        cmp $0x1, 0x2c(%r14)
        jb jit$$trace_exit_5
        cmp $0x4, 0x58(%r14)
        jnz .L8
.L3:
        mov 0x50(%r14), %rax
        mov $0x100000000, %rcx
        cqo 
        idiv %rcx
        test %rdx, %rdx
        jge jit$$trace_exit_6
        lea (%rdx), %rdx
        mov 0x10(%r14), %rcx
        mov %rdx, (%rcx)
        mov $0x4, 0x8(%rcx)
        mov $EG(vm_stack_top), %rax
        mov %r14, (%rax)
        mov 0x30(%r14), %r14
        mov $EG(current_execute_data), %rax
        mov %r14, (%rax)
        cmp $0x2, 0x80(%r14)
        setz %al
        movzx %al, %eax
        add $0x2, %eax
        mov %eax, 0x78(%r14)
        cmp $0x3, 0x78(%r14)
        jnz jit$$trace_exit_7
        mov 0x8(%r14), %r15
        mov $0x3, 0x58(%r15)
        mov $0x40f454d0, 0x60(%r15)
        mov $0x6, 0x68(%r15)
        mov $0x41675b98, (%r14)
        mov $0x0, 0x8(%r14)
        mov %r14, 0x30(%r15)
        mov %rsp, %rsi
        mov $0x1, 0x8(%rsi)
        mov $EG(current_execute_data), %rcx
        mov %r15, (%rcx)
        mov %r15, %rdi
        mov $0x5633ad3dbfb0, %rax
        call *%rax
        mov $EG(current_execute_data), %rax
        mov %r14, (%rax)
        test $0x1, 0x59(%r15)
        jnz .L9
.L4:
        test $0x1, 0x69(%r15)
        jnz .L10
.L5:
        mov $EG(vm_stack_top), %rax
        mov %r15, (%rax)
        mov $EG(exception), %rax
        cmp $0x0, (%rax)
        jnz JIT$$icall_throw
        mov $EG(vm_interrupt), %rax
        cmp $0x0, (%rax)
        jnz jit$$trace_exit_8
        cmp $0x4, 0x68(%r14)
        jnz jit$$trace_exit_9
        add $0x1, 0x60(%r14)
        jo .L11
.L6:
        mov $0x41675bd8, %r15
        mov $EG(vm_interrupt), %rax
        cmp $0x0, (%rax)
        jz .L1
        jmp JIT$$interrupt_handler
.L7:
        mov $0x416757a0, %rdi
        mov $zend_jit_init_func_run_time_cache_helper, %rax
        call *%rax
        mov 0x40(%r14), %rcx
        mov %rax, 0x10(%rcx)
        jmp .L2
.L8:
        lea 0x50(%r14), %rdi
        mov $0x416758a8, (%r14)
        mov $0x41675988, %rsi
        mov $zend_jit_verify_arg_slow, %rax
        call *%rax
        test %al, %al
        jnz .L3
        jmp JIT$$exception_handler
.L9:
        mov 0x50(%r15), %rdi
        sub $0x1, (%rdi)
        jnz .L4
        mov $0x41675b98, (%r14)
        mov $rc_dtor_func, %rax
        call *%rax
        jmp .L4
.L10:
        mov 0x60(%r15), %rdi
        sub $0x1, (%rdi)
        jnz .L5
        mov $0x41675b98, (%r14)
        mov $rc_dtor_func, %rax
        call *%rax
        jmp .L5
.L11:
        mov $0x43e0000000000000, %rax
        mov %rax, 0x60(%r14)
        mov $0x5, 0x68(%r14)
        jmp .L6

Test script:
---------------
<?php

function unsignedLong(int $offset): int
{
    $normalizedOffset = $offset % (2 ** 32);

    if ($normalizedOffset < 0) {
        $normalizedOffset += 2 ** 32;
    }

    return $normalizedOffset;
}

$offset = -0x100000000 + 2;

for ($i = 0; $i < 200; ++$i) {
    assert(unsignedLong($offset) === 2);
}



Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2021-07-06 12:48 UTC] nikic@php.net
-Status: Open +Status: Verified -Assigned To: +Assigned To: dmitry
 [2021-07-06 12:48 UTC] nikic@php.net
Also reproduces with function JIT. I see these lines:

    492d4da3:	mov $0x100000000, %rcx
    492d4dad:	cqo 
    492d4daf:	idiv %rcx
    492d4db2:	test %rdx, %rdx
	jge .L2
    492d4db7:	lea (%rdx), %rdx
.L2:

The lea looks suspicious to me, maybe there is an issue with encoding of immediates > 32-bit?
 [2021-07-12 05:48 UTC] hao dot sun at arm dot com
Based on my analysis, the root cause is "lea (%rdx), %rdx".

"lea" instruction would be used to optimize "add" or "sub" with constants in JIT/x86. See https://github.com/php/php-src/blob/master/ext/opcache/jit/zend_jit_x86.dasc#L4296-L4310

In this test case, the "lea" is generated at line 4300.

As @nikic suspected, it's an issue of encoding of immediate > 32bits.
AFAIK, for "lea reg, [reg + imm]" in x86_64, the imm field should not be > 32bits, otherwise, truncation would be done.

One possible solution may be adding one check, whether "Z_LVAL_P(Z_ZV(op2_addr))" can be represented by 32-bits, just before L4300.


Note that this bug doesn't affect JIT/arm64 in master branch, since "lea" optimization is not conducted in arm64.
 [2021-07-19 07:43 UTC] git@php.net
Automatic comment on behalf of dstogov
Revision: https://github.com/php/php-src/commit/9cd437138e2a7c172427876c8754b445fa1e1ab1
Log: Fixed bug #81225 (Wrong result with pow operator with JIT enabled)
 [2021-07-19 07:43 UTC] git@php.net
-Status: Verified +Status: Closed
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sun Oct 27 16:01:27 2024 UTC