Skip to content

Conversation

arnaud-lb
Copy link
Member

@arnaud-lb arnaud-lb commented Oct 6, 2025

Found while looking at GH-20051.

preload_load() accesses EG(function_table) and EG(class_table), but these may be uninitialized at this point. For instance, when preloading is done in a sub-process, these variables are uninitialized when the parent calls preload_load().

The update of EG(persistent_constants_count), EG(persistent_functions_count), EG(persistent_classes_count) is only necessary when preload_load() is invoked in accel_preload(), and preloading doesn't fork, because in this case, shutdown will truncate global symbol tables after the preloading request.

@arnaud-lb arnaud-lb marked this pull request as ready for review October 6, 2025 18:44
@arnaud-lb arnaud-lb requested a review from dstogov as a code owner October 6, 2025 18:44
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.

I didn't completely understand the problem, but I suppose, you are right.

@arnaud-lb
Copy link
Member Author

Attempting a better description of the problem:

There are two possible sequences during preloading:

  • Forking: (opcache.preload_user is set)
    • In the parent: fork(), waitpid(), preload_load()
    • In the child: php_request_startup(), execute/persist, php_request_shutdown()
  • Non-forking:
    • In the same process: php_request_startup(), execute/persist, preload_load(), php_request_shutdown()

preload_load() reads EG(class_table) and updates EG(persistent_classes_count).

EG(class_table) is only initialized by init_executor(), during php_request_startup(). Therefore the forking case may access an uninitialized EG(class_table) in the parent, as php_request_startup() is not called.

I think this has gone unnoticed because EG points to a freshly allocated large block, which is likely all zeros. But after an apache restart cycle, EG is more likely to be allocated from recycled memory.

So we can not update EG(persistent_classes_count) in preload_load(), as EG(class_table) may be uninitialized.

It is not necessary to update it in the forking case, as the next init_executor() call will initialize it, so we can remove that from preload_load().

However it's necessary in the non-forking case, as php_request_shutdown() would remove the preloaded symbols before init_executor(). So I moved the EG(persistent_classes_count) update just after the execute/persist step.

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.

2 participants