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
10 changes: 10 additions & 0 deletions .github/actions/configure-unit-tests/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: ./configure (unit tests)
description: Configure PHP with minimal settings for unit testing
runs:
using: composite
steps:
- shell: bash
run: |
set -x
./buildconf --force
./configure --disable-all --enable-embed=static
75 changes: 75 additions & 0 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Unit Tests
on:
push:
paths:
- 'main/network.c'
- 'tests/unit/**'
- '.github/workflows/unit-tests.yml'
branches:
- master
pull_request:
paths:
- 'main/network.c'
- 'tests/unit/**'
- '.github/workflows/unit-tests.yml'
branches:
- '**'
workflow_dispatch: ~

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.url || github.run_id }}
cancel-in-progress: true

env:
CC: ccache gcc
CXX: ccache g++

jobs:
UNIT_TESTS:
if: github.repository == 'php/php-src' || github.event_name == 'pull_request'
name: UNIT_TESTS_LINUX_X64
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- name: git checkout
uses: actions/checkout@v4

- name: Install dependencies
run: |
set -x
sudo apt-get update
sudo apt-get install -y \
libcmocka-dev \
autoconf \
gcc \
make \
unzip \
bison \
re2c \
locales \
ccache

- name: ccache
uses: hendrikmuhs/ccache-action@v1.2
with:
key: "unit-tests-${{hashFiles('main/php_version.h')}}"
append-timestamp: false
save: ${{ github.event_name != 'pull_request' }}

- name: ./configure (minimal build)
uses: ./.github/actions/configure-unit-tests

- name: make libphp.a
run: |
set -x
make -j$(/usr/bin/nproc) >/dev/null

- name: Run unit tests
run: |
set -x
cd tests/unit
make test

21 changes: 21 additions & 0 deletions Zend/zend_hrtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
# include <time.h>
# include <string.h>

ZEND_API clockid_t zend_hrtime_posix_clock_id = CLOCK_MONOTONIC;

#elif ZEND_HRTIME_PLATFORM_WINDOWS

# define WIN32_LEAN_AND_MEAN
Expand Down Expand Up @@ -66,5 +68,24 @@ void zend_startup_hrtime(void)

mach_timebase_info(&zend_hrtime_timerlib_info);

#elif ZEND_HRTIME_PLATFORM_POSIX

struct timespec ts;

#ifdef CLOCK_MONOTONIC_RAW
if (EXPECTED(0 == clock_gettime(CLOCK_MONOTONIC_RAW, &ts))) {
zend_hrtime_posix_clock_id = CLOCK_MONOTONIC_RAW;
return;
}
#endif

if (EXPECTED(0 == clock_gettime(zend_hrtime_posix_clock_id, &ts))) {
return;
}

// zend_error mechanism is not initialized at that point
fprintf(stderr, "No working CLOCK_MONOTONIC* found, this should never happen\n");
abort();

#endif
}
10 changes: 6 additions & 4 deletions Zend/zend_hrtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ ZEND_API extern double zend_hrtime_timer_scale;
# include <string.h>
ZEND_API extern mach_timebase_info_data_t zend_hrtime_timerlib_info;

#elif ZEND_HRTIME_PLATFORM_POSIX

ZEND_API extern clockid_t zend_hrtime_posix_clock_id;

#endif

#define ZEND_NANO_IN_SEC UINT64_C(1000000000)
Expand All @@ -92,10 +96,8 @@ static zend_always_inline zend_hrtime_t zend_hrtime(void)
return (zend_hrtime_t)mach_absolute_time() * zend_hrtime_timerlib_info.numer / zend_hrtime_timerlib_info.denom;
#elif ZEND_HRTIME_PLATFORM_POSIX
struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 };
if (EXPECTED(0 == clock_gettime(CLOCK_MONOTONIC, &ts))) {
return ((zend_hrtime_t) ts.tv_sec * (zend_hrtime_t)ZEND_NANO_IN_SEC) + ts.tv_nsec;
}
return 0;
clock_gettime(zend_hrtime_posix_clock_id, &ts);
return ((zend_hrtime_t) ts.tv_sec * (zend_hrtime_t)ZEND_NANO_IN_SEC) + ts.tv_nsec;
#elif ZEND_HRTIME_PLATFORM_HPUX
return (zend_hrtime_t) gethrtime();
#elif ZEND_HRTIME_PLATFORM_AIX
Expand Down
7 changes: 5 additions & 2 deletions ext/phar/zip.c
Original file line number Diff line number Diff line change
Expand Up @@ -1222,7 +1222,9 @@ static int phar_zip_applysignature(phar_archive_data *phar, struct _phar_zip_pas
entry.fp_type = PHAR_MOD;
entry.is_modified = 1;
if (entry.fp == NULL) {
efree(signature);
spprintf(pass->error, 0, "phar error: unable to create temporary file for signature");
php_stream_close(newfile);
return FAILURE;
}

Expand Down Expand Up @@ -1440,11 +1442,12 @@ void phar_zip_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def

phar_metadata_tracker_try_ensure_has_serialized_data(&phar->metadata_tracker, phar->is_persistent);
if (temperr) {
temperror:
if (error) {
spprintf(error, 4096, "phar zip flush of \"%s\" failed: %s", phar->fname, temperr);
}
efree(temperr);
temperror:
notemperror:
php_stream_close(pass.centralfp);
nocentralerror:
php_stream_close(pass.filefp);
Expand Down Expand Up @@ -1472,7 +1475,7 @@ void phar_zip_flush(phar_archive_data *phar, zend_string *user_stub, bool is_def
if (error) {
spprintf(error, 4096, "phar zip flush of \"%s\" failed: unable to write central-directory", phar->fname);
}
goto temperror;
goto notemperror;
}
}

Expand Down
7 changes: 7 additions & 0 deletions ext/standard/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -5044,6 +5044,11 @@ PHP_FUNCTION(array_unique)
ZVAL_UNDEF(&arTmp[i].b.val);
zend_sort((void *) arTmp, i, sizeof(struct bucketindex),
(compare_func_t) cmp, (swap_func_t) array_bucketindex_swap);

if (UNEXPECTED(EG(exception))) {
goto out;
}

/* go through the sorted array and delete duplicates from the copy */
lastkept = arTmp;
for (cmpdata = arTmp + 1; Z_TYPE(cmpdata->b.val) != IS_UNDEF; cmpdata++) {
Expand All @@ -5063,6 +5068,8 @@ PHP_FUNCTION(array_unique)
}
}
}

out:
pefree(arTmp, GC_FLAGS(Z_ARRVAL_P(array)) & IS_ARRAY_PERSISTENT);

if (in_place) {
Expand Down
12 changes: 12 additions & 0 deletions ext/standard/tests/array/gh20043.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
GH-20043 (array_unique assertion failure with RC1 array causing an exception on sort)
--FILE--
<?php
try {
array_unique([new stdClass, new stdClass], SORT_STRING | SORT_FLAG_CASE);
} catch (Error $e) {
echo $e->getMessage();
}
?>
--EXPECT--
Object of class stdClass could not be converted to string
32 changes: 32 additions & 0 deletions tests/unit/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
CC = gcc
CFLAGS = -g -Wall -I../../ -I../../Zend -I../../main -I../../TSRM -I. -I..
COMMON_LDFLAGS = ../../.libs/libphp.a -lcmocka -lpthread -lm -ldl -lresolv -lutil

# Update paths in .github/workflows/unit-tests.yml when adding new test to make it run in PR when such file changes
TESTS = main/test_network
main/test_network_SRC = main/test_network.c
main/test_network_LDFLAGS = $(COMMON_LDFLAGS) -Wl,--wrap=connect,--wrap=poll,--wrap=getsockopt,--wrap=gettimeofday


# Build all tests
all: $(TESTS)

# Build rule for each test
$(TESTS):
$(CC) $(CFLAGS) -o $@.out $($(basename $@)_SRC) $($(basename $@)_LDFLAGS)

# Run all tests
.PHONY: test
test: $(TESTS)
@echo "Running all tests..."
@for test in $(TESTS); do \
echo "Running $$test..."; \
$$test.out || exit 1; \
done

# Clean tests
.PHONY: clean
clean:
@for test in $(TESTS); do \
rm -f $$test.out; \
done
Loading