Bug 187263

Summary: g_list_remove() problem
Product: [Fedora] Fedora Reporter: Paul Osmialowski <newchief>
Component: glib2Assignee: Matthias Clasen <mclasen>
Status: CLOSED NOTABUG QA Contact:
Severity: medium Docs Contact:
Priority: medium    
Version: 5   
Target Milestone: ---   
Target Release: ---   
Hardware: i386   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2006-03-29 19:23:30 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Paul Osmialowski 2006-03-29 17:23:42 UTC
Description of problem:
It looks like g_list_remove() function frees pointer that is removed from the
list. This breaks compatibility with programs that assumes that the pointer
removed from the list should be freed. Since these programs call free on removed
pointer, they are aborted due to double-free and become unusable.

Version-Release number of selected component (if applicable):
glib2-2.10.1-1

How reproducible:


Steps to Reproduce:
1.
2.
3.
  
Actual results:


Expected results:


Additional info:
Tested program was the Stage 2.0.0a plugin for Player 1.6.5 robotics framework
(http://playerstage.sourceforge.net). Due to compatibility reasons it must be
compiled with gcc-3.x and g++-3.x compiler, I've used the one from FC5:
compat-gcc-32-3.2.3-55.fc5 and compat-gcc-32-c++-3.2.3-55.fc5
Stage plugin was crashing just after start of the Player process. After hours of
debugging I've found the place where call to free() function causes the problem.
It's just after g_list_remove() call. I've remarked g_list_remove() call and
program started to work (not exactly as it should but it didn't crash). Then
I've remarked call to free() function and restored g_list_remove() and whole
thing started to work properly.

Comment 1 Matthias Clasen 2006-03-29 18:20:05 UTC
g_list_remove does not free the data. It does free the list element, and has
always done so. Most likely the problem is in your code. Can you try to distill
it to a small testcase that still shows your problem ?

Comment 2 Paul Osmialowski 2006-03-29 19:16:07 UTC
The problem seems to be more complex. Example program works properly:

#include <glib.h>
#include <stddef.h>
#include <stdlib.h>                                                             

int main()
{
    GList * list = NULL;
    void * my_resource = malloc(2048);

    list = g_list_append(list, my_resource);
    list = g_list_remove(list, my_resource);
    free(my_resource);
    return 0;
}

Affected function in Stage plugin looks like this:

int stg_model_remove_property_callback( stg_model_t* mod,
                                        const char* propname,
                                        stg_property_callback_t callback )
{
  stg_property_t* prop = g_datalist_get_data( &mod->props, propname );

  if( ! prop )
    {
      PRINT_WARN2( "attempting to remove a callback from a nonexistent property
(%s:%s)",
                   mod->token, propname );
      return 1; // error
    }

  // else

  // find our callback in the list of stg_cbarg_t
  GList* el = NULL;

  // scan the list for the first matching callback
  for( el = g_list_first( prop->callbacks);
       el;
       el = el->next )
    {
      if( ((stg_cbarg_t*)el->data)->callback == callback )
        break;
    }

  if( el ) // if we found the matching callback, remove it
    prop->callbacks = g_list_remove( prop->callbacks, el->data);

  if( el && el->data )
    free( el->data ); //           THIS CALL CAUSES PROGRAM TO CRASH

  return 0; //ok
}
Since we know free was called, the el pointer can't be NULL, so this condition
must have been true:
if( ((stg_cbarg_t*)el->data)->callback == callback )
        break;
so I may expect that el->data holds valid address of stg_cbarg_t object. How it
could became invalud on free() call?
Strangely, the problem wasn't seen before we've upgraded FC3 to FC5, and is not
seen on our Gentoo machines with glib-2.8.6

Comment 3 Matthias Clasen 2006-03-29 19:23:30 UTC
well, the call to g_list_remove () will remove and free the first list element
whose data matches el->data. If that is el, then you are using freed memory
after that removal, and all bets are off. If you want to do things like this,
you need to do somthing like

if (el) {
 tmp = el->data;
 g_list_remove (prop->callbacks, tmp);
 if (tmp) free (tmp);
}