php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #56101 apc_clear_cache() make coredump
Submitted: 2004-06-17 06:32 UTC Modified: 2004-09-24 09:44 UTC
From: xuefer at 21cn dot com Assigned:
Status: Closed Package: APC (PECL)
PHP Version: 4CVS-2004-06-17 (stable) OS: linux
Private report: No CVE-ID: None
 [2004-06-17 06:32 UTC] xuefer at 21cn dot com
Description:
------------
lastest apc cvs -r HEAD
just tested 1 time
1 clear get 1 coredump.

#0  add_function (result=0xbfff9e34, op1=0x0, op2=0x0)
    at /home/oursky/src/php4/Zend/zend_operators.c:607
607		if (op1->type == IS_ARRAY && op2->type == IS_ARRAY) {

Backtrace:
---------------

#0  add_function (result=0xbfff9e34, op1=0x0, op2=0x0)
    at /home/oursky/src/php4/Zend/zend_operators.c:607
#1  0x405373d1 in execute (op_array=0x8345c00)
    at /home/oursky/src/php4/Zend/zend_execute.c:1143
#2  0x40746afe in my_execute ()
   from /usr/local/lib/php/extensions/no-debug-non-zts-20020429/apc.so



Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2004-06-21 06:02 UTC] xuefer at 21cn dot com
prove: after execute should not release cache
debug:
/* {{{ my_execute */
static void my_execute(zend_op_array* op_array TSRMLS_DC)
{
    printf("\nexec start %s\n", op_array->filename);
    old_execute(op_array TSRMLS_CC);
    printf("\nexec end %s\n", op_array->filename);
...
}
test case:

test.php
<?php
while (@ob_end_clean());
echo "in test\n";
include("./test2.php");
echo "in test\n";
?>
test2.php:
<?php
echo "in test2\n";
?>

run php-with-apc ./test.php
result:

exec start /t/WebDesign/php4-obj/test.php
in test

exec start /t/WebDesign/php4-obj/test2.php
in test2

exec end /t/WebDesign/php4-obj/test2.php
in test

exec end /t/WebDesign/php4-obj/test.php

so:
when exec end /t/WebDesign/php4-obj/test2.php, if cache is released
script "echo 'in test';" will give error if it call function in test2.php. e.g.
function abc() {}

my_execute should not release even 1 cache

before disabling the release code in my_execute
and with apc_deactivate() patch
"apcinfo.php: apc_cache_info();" showing only apcinfo.php Refcount=1, all other scripts refcount=0
when disabled
apc_clear_cache() works fine, with no crash
apcinfo.php and all it include/requires has refcount=1
sometime other scripts refcount=3 or 4 etc.
refcount is fine now

complete patch for apc_main.c:
Index: apc_main.c
===================================================================
RCS file: /repository/pecl/apc/apc_main.c,v
retrieving revision 3.8
diff -u -r3.8 apc_main.c
--- apc_main.c  25 Mar 2004 18:53:43 -0000      3.8
+++ apc_main.c  21 Jun 2004 10:02:25 -0000
@@ -184,7 +184,7 @@
     /* cache the compiler results */
     cache_compile_results(
         key,
-        h->opened_path,
+        h->opened_path ? h->opened_path : h->filename, // CGI/CLI have NULL opened_path
         apc_copy_op_array(NULL, op_array, apc_sma_malloc TSRMLS_CC),
         apc_copy_new_functions(num_functions, apc_sma_malloc TSRMLS_CC),
         apc_copy_new_classes(op_array, num_classes, apc_sma_malloc TSRMLS_CC));
@@ -198,6 +198,7 @@
 {
     old_execute(op_array TSRMLS_CC);
 
+#if 0
     if (apc_stack_size(APCG(cache_stack)) > 0) {
         apc_cache_entry_t* cache_entry =
             (apc_cache_entry_t*) apc_stack_top(APCG(cache_stack));
@@ -208,6 +209,7 @@
             apc_cache_release(APCG(cache), cache_entry);
         }
     }
+#endif
 }
 /* }}} */
 
@@ -277,6 +279,7 @@
 
 int apc_request_shutdown()
 {
+    apc_deactivate();
     return 0;
 }
 [2004-07-13 14:22 UTC] gschlossnagle@php.net
Your test case works fine for me.  I suspect it's poorly 
worded though.  In your subject you mention 
apc_clear_cache(), but it's not present in your test 
case.  Can you post an actual reproducing test case?

Also, apc serves really no useful purpose being run with 
the cli or cgi version.



 [2004-07-13 14:52 UTC] xuefer at 21cn dot com
sorry i can't give a short reproducable script
but my test case is just doing a "prove"
it prove why "after execute should not release cache"

the 2 IFs after "exec end" may not always true, but sometimes it will, no?
the test case just suppose the 2 IFs is true
and op_array is released, but it still in used
because op_array contains:
1. functions
2. clases
3. direct op codes, which is execute()ed whenever the script is include()ed, but it's released by apc in my_executed()
4. others

op_array should only be released when the whole request is shutdown, not after executing each op_array.
some other phpfiles need those functions and classes

i don't know much about php script engine
some of my knowledge is just guessing
any correction to my note is welcomed :)
 [2004-07-13 15:10 UTC] xuefer at 21cn dot com
3. direct op codes
shold be op_array->opcodes

about apc_clear_cache:
test.php
<?php
while (@ob_end_clean());
echo "in test\n";
include("./test2.php");
echo "in test\n";
// when sleep, do apc_clear_cache() in another httpd process(must in same cache)
sleep(10);
call_some_func_in_test2(); // should crash
?>

==========
        /* compare pointers to determine if op_array's are same */
        if (cache_entry->op_array->opcodes == op_array->opcodes) {
            apc_stack_pop(APCG(cache_stack));
            apc_cache_release(APCG(cache), cache_entry);
        }
==========
i don't know when the above if() will be eval to true. but apc_cache_release() should never be called after my_execute() (in php it's include();)


>Also, apc serves really no useful purpose being run with 
>the cli or cgi version.
so php.ini has to be config separated so httpd enable apc and cli/cgi disable apc?
it's not good.
 [2004-07-13 15:13 UTC] gschlossnagle@php.net
>>Also, apc serves really no useful purpose being run 
>with 
>>the cli or cgi version.
>so php.ini has to be config separated so httpd enable ?
>apc and cli/cgi
>disable apc?
>it's not good.


No, it just provides no useful function.
 [2004-07-13 15:18 UTC] xuefer at 21cn dot com
sorry, so many times submit :)

call_some_func_in_test2();
call_some_func_in_test2 is a function in test2.php
it may crash only (cache_entry->op_array->opcodes == op_array->opcodes)
but may not

execute steps:
1. execute: test1.php op_array->opcodes
2. include: test2.php op_array->opcodes
3. release op_array
4. another process/thread do apc_clear_cache() and override the op_array of test2.php(cos it's released)
5. continue: test1.php op_array->opcodes, call function in test2.php, crash
 [2004-07-13 15:25 UTC] xuefer at 21cn dot com
>No, it just provides no useful function.
i know apc gives nothing useful for cli/cgi

we have 3 choices
1. configure php.ini separate for httpd/cli/cgi, which is not easy
2. auto disable itself when she notice the api is cli/cgi, which don't need config
3. make apc run with cli/cgi no buggy, no side effect

which one do u like?
turck-mmcache choose 2

but i guess, cli is useful for debugging apc for developers
 [2004-07-14 03:14 UTC] xuefer at 21cn dot com
may i ask:
====
        /* compare pointers to determine if op_array's are same */
        if (cache_entry->op_array->opcodes == op_array->opcodes) {
            apc_stack_pop(APCG(cache_stack));
            apc_cache_release(APCG(cache), cache_entry);
        }
====
when will this apc_cache_release() is called? i actually don't know

reproduce suggestion:
if u have lowlevel debug function in C, which cando:
inside apc_cache_release(), after released the cache, set the memory block into "unreadable"

---then-----:
test case:
test.php
<?php
while (@ob_end_clean());
echo "in test\n";
include("./test2.php");
echo "in test\n";
test2(); // this should produce a memory cannot "READ" error
?>
test2.php:
<?php
echo "in test2\n";
function test2()
{
echo "in test2\n";
}
?>


btw, in mmcache, there's no release after each execute()
if apc do release op_array exactly same as Zend do, it should be fine :)
read code in Zend/zend_execute.c, there's

ZEND_API void execute(zend_op_array *op_array TSRMLS_DC)
......
case ZEND_RETURN: {
......
	free_alloca(EX(Ts));
	EG(in_execution) = EX(original_in_execution);
	EG(current_execute_data) = EX(prev_execute_data);
	return;

only tmp vars is released
 [2004-09-24 09:44 UTC] xuefer at 21cn dot com
this bug seems fixed in CVS
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Thu Apr 18 22:01:25 2019 UTC