Hello internals,
I’ve been trying to chase down a very subtle bug in 8.4 that only happens when OPcache is enabled (I'm trying to create a reproducer to file an actual bug). From what I can tell, OPcache doesn’t zero out cache slots, so occasionally, a cache slot will contain garbage that happens to pass asserts/checks that causes the program to behave incorrectly. Without OPcache, cache slots are always NULL
if not set.
It's quite rare to end up in that situation, though, but I was wondering if anyone has any opinions on this?
— Rob
Hi Rob
I’ve been trying to chase down a very subtle bug in 8.4 that only happens when OPcache is enabled (I'm trying to create a reproducer to file an actual bug). From what I can tell, OPcache doesn’t zero out cache slots, so occasionally, a cache slot will contain garbage that happens to pass asserts/checks that causes the program to behave incorrectly. Without OPcache, cache slots are always
NULL
if not set.It's quite rare to end up in that situation, though, but I was wondering if anyone has any opinions on this?
I don't believe this is accurate. If we're talking about the cache
used within the VM, "cache slots" are a contiguous list of arbitrary
void pointers. The compiler tracks how many cache slots each function
needs, but the actual array of pointers is allocated at runtime, in
init_func_run_time_cache_i(), when the function is executed for the
first time on each request. init_func_run_time_cache_i() zeros the
cache with memset. If it wouldn't, we'd run into issues quickly,
because a non-0 cache is generally interpreted as a primed cache.
Potentially, you might also be referring to the pointer map, which is
a related concept. But this map is also zero'ed for each request (in
zend_activate()).
If you think you might be dealing with uninitialized memory in your
case, MemorySanitizer or Valgrind may help.
Does that help?
Ilija
Hi Rob
I’ve been trying to chase down a very subtle bug in 8.4 that only happens when OPcache is enabled (I'm trying to create a reproducer to file an actual bug). From what I can tell, OPcache doesn’t zero out cache slots, so occasionally, a cache slot will contain garbage that happens to pass asserts/checks that causes the program to behave incorrectly. Without OPcache, cache slots are always
NULL
if not set.It's quite rare to end up in that situation, though, but I was wondering if anyone has any opinions on this?
I don't believe this is accurate. If we're talking about the cache
used within the VM, "cache slots" are a contiguous list of arbitrary
void pointers. The compiler tracks how many cache slots each function
needs, but the actual array of pointers is allocated at runtime, in
init_func_run_time_cache_i(), when the function is executed for the
first time on each request. init_func_run_time_cache_i() zeros the
cache with memset. If it wouldn't, we'd run into issues quickly,
because a non-0 cache is generally interpreted as a primed cache.Potentially, you might also be referring to the pointer map, which is
a related concept. But this map is also zero'ed for each request (in
zend_activate()).If you think you might be dealing with uninitialized memory in your
case, MemorySanitizer or Valgrind may help.Does that help?
Ilija
Thanks Ilija,
You are correct! This issue appears to be an "off-by-one" type error, so the data in the cache slot is just something unexpected, which looks like garbage. I think I finally found the issue, maybe... in zend_compile_static_call where the logic to determine the number of cache slots is different from the logic to set the cache slots. At least, that is where I am at right now. But that you for your help!
— Rob