Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions ext/openssl/openssl_x509_read.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,19 @@

use PHPCompiler\Frame;
use PHPCompiler\Func\Internal;
use PHPCompiler\JIT\BasicBlockHelper;
use PHPCompiler\JIT\Context;
use PHPCompiler\JIT\ExceptionBridge;
use PHPCompiler\JIT\JitValueBox;
use PHPCompiler\JIT\Variable as JITVariable;
use PHPLLVM\Value;

/**
* openssl_x509_read() — parse PEM into OpenSSLCertificate (php-src ext/openssl/xp.c; #7268, #6274).
*
* JIT/AOT leftover #33497: catchable argc/TypeError for non-(OpenSSLCertificate|string) args
* (peer openssl_pkey_free #33487 / openssl_x509_free #33492). Happy-path string/object → certificate
* still needs object emission (VM-only today).
*/
final class openssl_x509_read extends Internal
{
Expand All @@ -37,8 +44,48 @@ public function execute(Frame $frame): void

public function call(Context $context, JITVariable ...$args): Value
{
if (!$this->requireExactJitArgCount($context, $args, 'openssl_x509_read', 1)) {
return self::jitReturnNull($context);
}

$badLabel = self::compileTimeNonCertificateOrStringLabel($args[0]);
if (null !== $badLabel) {
ExceptionBridge::emitTypeErrorAndAbort(
$context,
'openssl_x509_read(): Argument #1 ($certificate) must be of type OpenSSLCertificate|string, '
.$badLabel.' given'
);
BasicBlockHelper::ensureOpenInsertBlock($context, 'openssl_x509_read_te_cont');

return self::jitReturnNull($context);
}

// String / OpenSSLCertificate object emission remains VM-only for thin AOT (#33497).
throw new \LogicException(
'openssl_x509_read() is not implemented for JIT in this compiler build (issue #7268)'
);
}

/** @return non-empty-string|null Zend type label when the arg cannot be OpenSSLCertificate|string */
private static function compileTimeNonCertificateOrStringLabel(JITVariable $arg): ?string
{
if (JITVariable::TYPE_NULL === $arg->type || ($arg->isNullConstant ?? false)) {
return 'null';
}

return match ($arg->type) {
JITVariable::TYPE_NATIVE_BOOL => 'bool',
JITVariable::TYPE_NATIVE_LONG => 'int',
JITVariable::TYPE_NATIVE_DOUBLE => 'float',
JITVariable::TYPE_HASHTABLE => 'array',
default => null,
};
}

private static function jitReturnNull(Context $context): Value
{
$slot = JitValueBox::alloc($context);

return JitValueBox::pointer($context, $slot);
}
}
17 changes: 17 additions & 0 deletions test/repro/issue_33497_openssl_x509_read_aot.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

// #33497 leftover of #7268 — TypeError/argc gate; happy-path PEM→OpenSSLCertificate still VM-only.
try {
openssl_x509_read(null);
echo "null-type=ok\n";
} catch (Throwable $e) {
echo 'null-type=', $e::class, "\n";
}
try {
openssl_x509_read();
echo "argc=ok\n";
} catch (Throwable $e) {
echo 'argc=', $e::class, "\n";
}
74 changes: 74 additions & 0 deletions test/unit/OpensslX509ReadAotTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

declare(strict_types=1);

namespace PHPCompiler;

use PHPUnit\Framework\TestCase;

/**
* AOT: openssl_x509_read() leftover of #7268 (#33497).
*
* @see php-src ext/openssl/openssl.c / xp.c PHP_FUNCTION(openssl_x509_read)
*
* @group aot-lint
*/
final class OpensslX509ReadAotTest extends TestCase
{
private const EXPECTED = "null-type=TypeError\nargc=ArgumentCountError\n";

public function testVmTypeErrorAndArgc(): void
{
$runtime = new Runtime();
$code = file_get_contents(dirname(__DIR__).'/repro/issue_33497_openssl_x509_read_aot.php');
$this->assertNotFalse($code);
ob_start();
$runtime->run($runtime->parseAndCompile($code, 'issue_33497_openssl_x509_read_aot.php'));
$out = (string) ob_get_clean();
$this->assertSame(self::EXPECTED, $out);
}

/**
* @group llvm
* @group aot
*/
public function testAotTypeErrorAndArgcMatchVm(): void
{
if (!LlvmToolchain::hasLibrary(dirname(__DIR__, 2))) {
$this->markTestSkipped('LLVM 9 toolchain not available');
}
$root = dirname(__DIR__, 2);
$src = $root.'/test/repro/issue_33497_openssl_x509_read_aot.php';
$runtime = new Runtime();
$code = file_get_contents($src);
$this->assertNotFalse($code);
ob_start();
$runtime->run($runtime->parseAndCompile($code, 'issue_33497_openssl_x509_read_aot.php'));
$vmOut = (string) ob_get_clean();
$this->assertSame(self::EXPECTED, $vmOut);

$bin = sys_get_temp_dir().'/phpc_ossl_x509_read_'.getmypid().'.bin';
$compile = 'env PHP_COMPILER_HELPER_RUNTIME_O=1 '.escapeshellarg(PHP_BINARY).' '
.escapeshellarg($root.'/bin/compile.php')
.' -o '.escapeshellarg($bin).' '.escapeshellarg($src).' 2>&1';
exec($compile, $compileOut, $compileRc);
$this->assertSame(0, $compileRc, implode("\n", $compileOut));
$this->assertFileExists($bin);
try {
$runOut = [];
exec(escapeshellarg($bin).' 2>/dev/null', $runOut, $runRc);
$this->assertSame(0, $runRc, implode("\n", $runOut));
$this->assertSame($vmOut, implode("\n", $runOut)."\n");
} finally {
@unlink($bin);
}
}

public function testCallEmitsCatchableTypeErrorNotOnlyLogicException(): void
{
$src = (string) file_get_contents(dirname(__DIR__, 2).'/ext/openssl/openssl_x509_read.php');
$this->assertStringContainsString('ExceptionBridge::emitTypeErrorAndAbort', $src);
$this->assertStringContainsString('requireExactJitArgCount', $src);
$this->assertFileDoesNotExist(dirname(__DIR__, 2).'/lib/AOT/runtime/openssl_x509_read.c');
}
}
Loading