[PECL-CVS] [pecl-web_services-oauth] master: Merge pull request #38 from php/fix-callback-exception-segfault

[email protected] (Rasmus Lerdorf via GitHub) Sat, 4 Apr 2026 21:39:40 +0000
Newsgroups php.pecl.cvs
Message-ID <[email protected]>
Author: Rasmus Lerdorf (rlerdorf)
Committer: GitHub (web-flow)
Pusher: rlerdorf
Date: 2026-04-04T22:39:37+01:00

Commit: https://github.com/php/pecl-web_services-oauth/commit/af843166d35224ea83bd536fa6575702ae5ea377
Raw diff: https://github.com/php/pecl-web_services-oauth/commit/af843166d35224ea83bd536fa6575702ae5ea377.diff

Merge pull request #38 from php/fix-callback-exception-segfault

fixes #27

Changed paths:
  A  .github/workflows/ci.yml
  A  tests/oauthprovider_009.phpt
  M  provider.c
  M  provider.h


Diff:

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..db2e9a7
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,43 @@
+name: CI
+on:
+  pull_request:
+    branches:
+      - master
+  push:
+    branches:
+      - master
+  workflow_dispatch:
+
+jobs:
+  build:
+    name: PHP ${{ matrix.version }} on ${{ matrix.os }}
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest]
+        version: ['8.1', '8.2', '8.3', '8.4', '8.5']
+    steps:
+      - name: Setup PHP
+        uses: shivammathur/setup-php@v2
+        with:
+          php-version: ${{ matrix.version }}
+
+      - uses: actions/checkout@v4
+
+      - name: Install dependencies
+        run: |
+          sudo apt-get update
+          sudo apt-get install -y libcurl4-openssl-dev
+
+      - name: Run phpize
+        run: phpize
+
+      - name: Configure
+        run: ./configure
+
+      - name: Build
+        run: make
+
+      - name: Run tests
+        run: make test
diff --git a/provider.c b/provider.c
index 9d54dca..da09647 100644
--- a/provider.c
+++ b/provider.c
@@ -383,6 +383,14 @@ static zval *oauth_provider_call_cb(INTERNAL_FUNCTION_PARAMETERS, int type) /* {
 
 	zval_ptr_dtor(&args);
 
+	/* If the callback threw an exception, return_value may be in an
+	 * undefined state. Reset it and return NULL so callers don't try
+	 * to use or destroy a corrupt zval. See issue #27. */
+	if (EG(exception)) {
+		ZVAL_UNDEF(return_value);
+		return NULL;
+	}
+
 	return return_value;
 }
 /* }}} */
diff --git a/provider.h b/provider.h
index 90ec01a..f4d7e2e 100644
--- a/provider.h
+++ b/provider.h
@@ -26,7 +26,12 @@
 	OAUTH_PROVIDER_COPY_ZVAL_FROM_PZVAL(dest, src, 0)
 
 #define OAUTH_PROVIDER_CALL_CB(pt, m) \
-	ZVAL_DUP(return_value, oauth_provider_call_cb(pt, m)); \
+	do { \
+		zval *_cb_ret = oauth_provider_call_cb(pt, m); \
+		if (_cb_ret) { \
+			ZVAL_DUP(return_value, _cb_ret); \
+		} \
+	} while (0)
 
 #define OAUTH_PROVIDER_FREE_FCALL_INFO(o) \
 	if(o) { \
diff --git a/tests/oauthprovider_009.phpt b/tests/oauthprovider_009.phpt
new file mode 100644
index 0000000..51e6e93
--- /dev/null
+++ b/tests/oauthprovider_009.phpt
@@ -0,0 +1,45 @@
+--TEST--
+OAuthProvider callback exception handling (issue #27)
+--SKIPIF--
+<?php
+if (!extension_loaded("oauth")) die("skip oauth extension not loaded");
+?>
+--FILE--
+<?php
+$params = [
+    "oauth_consumer_key" => "key",
+    "oauth_signature" => "sig",
+    "oauth_nonce" => "nonce",
+    "oauth_timestamp" => "12345"
+];
+
+$p = new OAuthProvider($params);
+$p->consumerHandler(function() { throw new RuntimeException("consumer exception"); });
+$p->tokenHandler(function() { throw new RuntimeException("token exception"); });
+$p->timestampNonceHandler(function() { throw new RuntimeException("nonce exception"); });
+
+try {
+    $p->callconsumerHandler();
+} catch (RuntimeException $e) {
+    echo $e->getMessage() . "\n";
+}
+
+try {
+    $p->calltokenHandler();
+} catch (RuntimeException $e) {
+    echo $e->getMessage() . "\n";
+}
+
+try {
+    $p->callTimestampNonceHandler();
+} catch (RuntimeException $e) {
+    echo $e->getMessage() . "\n";
+}
+
+echo "OK\n";
+?>
+--EXPECT--
+consumer exception
+token exception
+nonce exception
+OK