Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

double free when deleting nested XML elements using // (descendant-or-self) #319

Closed
GeoffWilliams opened this issue Nov 24, 2015 · 2 comments

Comments

@GeoffWilliams
Copy link
Contributor

Overview

Double free from libaugeas with lens Xml.lns when deleting nested XML elements collected using // (descendant-or-self).

Software versions

  • CentOS Linux release 7.0.1406 (Core) x86_64
  • Augeas 495358a (Wed Nov 18 11:22:44 2015)
  • Ruby ruby 2.1.7p400 (2015-08-18 revision 51632) [x86_64-linux]
  • ruby-augeas (0.5.0)

background

Augeas is being used to remove all Realm elements from Tomcat's server.xml file using the ruby-augeas API like this:

@aug.rm("/files/opt/apache-tomcat/tomcat6/conf/server.xml/Server//Realm")

The operation itself seems to succeed but there is an error from libaugeas when the library attempts to free the memory being used.

Originally this problem was encountered invoking augeas through Puppet but I was able to reproduce the problem using augtool, see testcase.

External bug report: https://tickets.puppetlabs.com/browse/MODULES-1721

Error details

The above Ruby function enters libaugeas in aug_rm() and results in the following backtrace when running under puppet:

#0  __lll_lock_wait_private () at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:95
#1  0x00007f0edd206f66 in _L_lock_12192 () at malloc.c:5200
#2  0x00007f0edd2044e1 in __GI___libc_malloc (bytes=56) at malloc.c:2866
#3  0x00007f0ede388388 in _dl_map_object_deps (map=map@entry=0x4593280,
    preloads=preloads@entry=0x0, npreloads=npreloads@entry=0, trace_mode=trace_mode@entry=0,
    open_mode=open_mode@entry=-2147483648) at dl-deps.c:515
#4  0x00007f0ede38e92c in dl_open_worker (a=a@entry=0x12f2408) at dl-open.c:265
#5  0x00007f0ede38a314 in _dl_catch_error (objname=objname@entry=0x12f23f8,
    errstring=errstring@entry=0x12f2400, mallocedp=mallocedp@entry=0x12f23f0,
    operate=operate@entry=0x7f0ede38e7b0 <dl_open_worker>, args=args@entry=0x12f2408)
    at dl-error.c:177
#6  0x00007f0ede38e25b in _dl_open (file=0x7f0edd2ff0c6 "libgcc_s.so.1", mode=-2147483647,
    caller_dlopen=<optimized out>, nsid=-2, argc=6, argv=0x7fff2bb985a8, env=0x4869f90)
    at dl-open.c:656
#7  0x00007f0edd2b4be2 in do_dlopen (ptr=ptr@entry=0x12f2610) at dl-libc.c:87
#8  0x00007f0ede38a314 in _dl_catch_error (objname=0x12f25f0, errstring=0x12f2600,
    mallocedp=0x12f25e0, operate=0x7f0edd2b4ba0 <do_dlopen>, args=0x12f2610) at dl-error.c:177
#9  0x00007f0edd2b4ca2 in dlerror_run (args=0x12f2610, operate=0x7f0edd2b4ba0 <do_dlopen>)
    at dl-libc.c:46
#10 __GI___libc_dlopen_mode (name=name@entry=0x7f0edd2ff0c6 "libgcc_s.so.1",
    mode=mode@entry=-2147483647) at dl-libc.c:163
#11 0x00007f0edd28e265 in init () at ../sysdeps/x86_64/backtrace.c:52
#12 0x00007f0eddc8ebe0 in pthread_once ()
---Type <return> to continue, or q <return> to quit---
    at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_once.S:103
#13 0x00007f0edd28e37c in __GI___backtrace (array=array@entry=0x7f0ede377240 <trace.11803>,
    size=size@entry=1024) at ../sysdeps/x86_64/backtrace.c:103
#14 0x00007f0ede080d1c in rb_print_backtrace () at vm_dump.c:690
#15 rb_vm_bugreport () at vm_dump.c:746
#16 0x00007f0eddf156c3 in report_bug (
    file=file@entry=0x4588f70 "/opt/puppetlabs/puppet/lib/ruby/vendor_ruby/puppet/provider/augeas/augeas.rb", line=<optimized out>, fmt=fmt@entry=0x7f0ede0c091c "Segmentation fault at %p",
    args=args@entry=0x12f2958) at error.c:312
#17 0x00007f0eddf16313 in rb_bug (fmt=fmt@entry=0x7f0ede0c091c "Segmentation fault at %p")
    at error.c:339
#18 0x00007f0eddff8983 in sigsegv (sig=<optimized out>, info=0x12f2bb0, ctx=<optimized out>)
    at signal.c:824
#19 <signal handler called>
#20 malloc_consolidate (av=av@entry=0x7f0edd53e760 <main_arena>) at malloc.c:4115
#21 0x00007f0edd202429 in _int_malloc (av=av@entry=0x7f0edd53e760 <main_arena>,
    bytes=bytes@entry=8192) at malloc.c:3400
#22 0x00007f0edd204f0a in __libc_calloc (n=<optimized out>, elem_size=<optimized out>)
    at malloc.c:3208
#23 0x00007f0edd1f8d63 in __GI_open_memstream (bufloc=bufloc@entry=0x7fff2bb96290,
    sizeloc=sizeloc@entry=0x7fff2bb962a0) at memstream.c:85
#24 0x00007f0edd2743ea in __GI___vsyslog_chk (pri=11, pri@entry=3, flag=flag@entry=-1,
    fmt=fmt@entry=0x7f0edd302b88 "*** Error in `%s': %s: 0x%s ***\n",
---Type <return> to continue, or q <return> to quit---
    ap=ap@entry=0x7fff2bb96578) at ../misc/syslog.c:167
#25 0x00007f0edd274a00 in __vsyslog (pri=pri@entry=3,
    fmt=fmt@entry=0x7f0edd302b88 "*** Error in `%s': %s: 0x%s ***\n",
    ap=ap@entry=0x7fff2bb96578) at ../misc/syslog.c:324
#26 0x00007f0edd1fa0f6 in __libc_message (do_abort=do_abort@entry=2,
    fmt=fmt@entry=0x7f0edd302b88 "*** Error in `%s': %s: 0x%s ***\n")
    at ../sysdeps/unix/sysv/linux/libc_fatal.c:166
#27 0x00007f0edd20156d in malloc_printerr (ptr=<optimized out>,
    str=0x7f0edd302bf8 "double free or corruption (fasttop)", action=3) at malloc.c:4972
#28 _int_free (av=0x7f0edd53e760 <main_arena>, p=<optimized out>, have_lock=0)
    at malloc.c:3804
#29 0x00007f0ed10d15d9 in free_tree_node (tree=tree@entry=0x4cdac50) at augeas.c:651
#30 0x00007f0ed10d2df6 in free_tree (tree=0x4cefae0) at augeas.c:1134
#31 0x00007f0ed10d2de7 in free_tree (tree=0x4cdac40) at augeas.c:1133
#32 0x00007f0ed10d2de7 in free_tree (tree=0xc0) at augeas.c:1133
#33 0x00007f0ed10d2e95 in tree_unlink_raw (tree=0x4cdab50) at augeas.c:662
#34 0x00007f0ed10d3752 in tree_rm (p=p@entry=0x49d5970) at augeas.c:1167
#35 0x00007f0ed10d37d3 in aug_rm (aug=aug@entry=0x476c160, path=<optimized out>)
    at augeas.c:1182
#36 0x00007f0ed1318e45 in augeas_rm (s=<optimized out>, path=61564760,
    sibling=<optimized out>) at ext/augeas/_augeas.c:165
#37 0x00007f0ede0781d1 in vm_call_cfunc_with_frame (ci=<optimized out>,
    reg_cfp=0x7f0ede58a110, th=0x12845b0) at vm_insnhelper.c:1510
---Type <return> to continue, or q <return> to quit---
#38 vm_call_cfunc (ci=<optimized out>, reg_cfp=0x7f0ede58a110, th=0x12845b0)
    at vm_insnhelper.c:1600
#39 vm_call_method (th=0x12845b0, cfp=0x7f0ede58a110, ci=<optimized out>)
    at vm_insnhelper.c:1788
#40 0x00007f0ede06f284 in vm_exec_core (th=th@entry=0x12845b0, initial=initial@entry=0)
    at insns.def:1028
#41 0x00007f0ede07314f in vm_exec (th=th@entry=0x12845b0) at vm.c:1421
#42 0x00007f0ede0762ba in invoke_block_from_c (th=0x12845b0, block=<optimized out>,
    self=<optimized out>, argc=<optimized out>, argv=<optimized out>,
    blockptr=<optimized out>, cref=0x0, defined_class=63658160) at vm.c:817
#43 0x00007f0ede077359 in vm_yield (argv=<optimized out>, argc=<optimized out>,
    th=<optimized out>) at vm.c:856
#44 rb_yield_0 (argv=<optimized out>, argc=<optimized out>) at vm_eval.c:944
#45 rb_yield (val=61566480) at vm_eval.c:954
#46 0x00007f0eddecc042 in rb_ary_each (array=61566520) at array.c:1796
#47 0x00007f0ede0781d1 in vm_call_cfunc_with_frame (ci=<optimized out>,
    reg_cfp=0x7f0ede58a1b0, th=0x12845b0) at vm_insnhelper.c:1510
#48 vm_call_cfunc (ci=<optimized out>, reg_cfp=0x7f0ede58a1b0, th=0x12845b0)
    at vm_insnhelper.c:1600
#49 vm_call_method (th=0x12845b0, cfp=0x7f0ede58a1b0, ci=<optimized out>)
    at vm_insnhelper.c:1788
#50 0x00007f0ede06fd45 in vm_exec_core (th=th@entry=0x12845b0, initial=initial@entry=0)
    at insns.def:999
---Type <return> to continue, or q <return> to quit---
#51 0x00007f0ede07314f in vm_exec (th=th@entry=0x12845b0) at vm.c:1421
#52 0x00007f0ede0762ba in invoke_block_from_c (th=0x12845b0, block=<optimized out>,
    self=<optimized out>, argc=<optimized out>, argv=<optimized out>,
    blockptr=<optimized out>, cref=0x0, defined_class=38025000) at vm.c:817
#53 0x00007f0ede077359 in vm_yield (argv=<optimized out>, argc=<optimized out>,
    th=<optimized out>) at vm.c:856
#54 rb_yield_0 (argv=<optimized out>, argc=<optimized out>) at vm_eval.c:944
#55 rb_yield (val=61557360) at vm_eval.c:954
#56 0x00007f0eddecc042 in rb_ary_each (array=64397600) at array.c:1796
#57 0x00007f0ede06a514 in vm_call_cfunc_with_frame (ci=<optimized out>,
    reg_cfp=0x7f0ede58a340, th=0x12845b0) at vm_insnhelper.c:1510
#58 vm_call_cfunc (th=0x12845b0, reg_cfp=0x7f0ede58a340, ci=<optimized out>)
    at vm_insnhelper.c:1600
#59 0x00007f0ede06fd45 in vm_exec_core (th=th@entry=0x12845b0, initial=initial@entry=0)
    at insns.def:999
#60 0x00007f0ede07314f in vm_exec (th=th@entry=0x12845b0) at vm.c:1421
#61 0x00007f0ede074434 in vm_call0_body (th=0x12845b0, ci=ci@entry=0x7fff2bb97d40,
    argv=0x7f0ede48b578, argv@entry=0x7fff2bb97d80) at vm_eval.c:171
#62 0x00007f0ede075017 in vm_call0 (defined_class=41544480, me=<optimized out>,
    argv=argv@entry=0x7fff2bb97d80, argc=argc@entry=32526, id=<optimized out>,
    recv=<optimized out>, th=<optimized out>) at vm_eval.c:50
#63 rb_vm_call (th=<optimized out>, recv=<optimized out>, id=<optimized out>,
    argc=argc@entry=1, argv=argv@entry=0x7f0ede48b578, me=<optimized out>,
---Type <return> to continue, or q <return> to quit---
    defined_class=41544480) at vm_eval.c:247
#64 0x00007f0eddf26f66 in rb_method_call_with_block (argc=1, argv=0x7f0ede48b578,
    method=<optimized out>, pass_procval=8) at proc.c:1820
#65 0x00007f0ede06a514 in vm_call_cfunc_with_frame (ci=<optimized out>,
    reg_cfp=0x7f0ede58a570, th=0x12845b0) at vm_insnhelper.c:1510
#66 vm_call_cfunc (th=0x12845b0, reg_cfp=0x7f0ede58a570, ci=<optimized out>)
    at vm_insnhelper.c:1600
#67 0x00007f0ede06f284 in vm_exec_core (th=th@entry=0x12845b0, initial=initial@entry=0)
    at insns.def:1028
#68 0x00007f0ede07314f in vm_exec (th=th@entry=0x12845b0) at vm.c:1421
#69 0x00007f0ede07b396 in rb_iseq_eval_main (iseqval=iseqval@entry=23662040) at vm.c:1660
#70 0x00007f0eddf1b88f in ruby_exec_internal (n=0x1690dd8) at eval.c:253
#71 0x00007f0eddf1d9ad in ruby_exec_node (n=n@entry=0x1690dd8) at eval.c:318
#72 0x00007f0eddf2017c in ruby_run_node (n=0x1690dd8) at eval.c:310
#73 0x00000000004008bb in main ()

Once I'd developed a stand-alone testcase I was able to add some print statements and step through the code with GDB and I found that:

  • Double-free is occurring inside free_tree_node() when free() is used against a dangling pointer
  • If I comment out the call to free_tree_node(del) and recompile the library code works as expected but will leak memory
  • The tree-walking/free'ing code is sound
  • For the particular test data I'm using, 4 individual trees of Realm elements are built, one for each present in the input XML file
  • Each of these trees is disposed separately by tree_rm() making a call to tree_unlink_raw(del[i])
  • Each parsed tree has a unique memory pointer, e.g, I see the following calls being made:
    • tree_unlink_raw(0x2740920)
    • tree_unlink_raw(0x273ead0)
    • tree_unlink_raw(0x273fb30) -- results in a segfault
  • I can see the a pointer being freed twice inside free_tree_node() and the pointer value matches an element inside one of the previous trees

Narrative (added printf statements)

ENTERING aug_rm(), path is /files/opt/apache-tomcat/tomcat6/conf/server.xml/Server//Realm
entering tree_rm()**** tree count: 4
================= delete tree 0 ==========
tree_unlink_raw(0x2740920)
free_tree(0x273fb30)
free_tree(0x273ff00)
free_tree(0x273dcc0)
free_tree((nil))
free_tree_node(0x273dcc0) == org.apache.catalina.realm.JNDIRealm
free_tree_node(0x273ff00) == (null)           ^
free_tree_node(0x273fb30) == (null)           |
free_tree_node(0x2740920) == (null)           |
================= delete tree 1 ==========    |
tree_unlink_raw(0x273ead0)                    |
free_tree(0x273ef40)                          |
free_tree(0x273e960)                          | already freed here
free_tree(0x273d7b0)                          |
free_tree((nil))                              |
free_tree_node(0x273d7b0) == ldap://localhost |
free_tree_node(0x273e960) == (null)           |
free_tree_node(0x273ef40) == (null)           |
free_tree_node(0x273ead0) == (null)           |
================= delete tree 2 ==========    |
tree_unlink_raw(0x273fb30)                    |
free_tree(0x273ff00)                          |
free_tree(0x273dcc0)                          |
free_tree((nil))                              |
free_tree_node(0x273dcc0) ==                  |
free_tree_node(0x273ff00) == (null)           |
free_tree(0x273ff00)                          |
free_tree(0x273dcc0)                          |
free_tree((nil))                              |
free_tree_node(0x273dcc0) == p?s         <------- garbage at dangling pointer
*** Error in `/opt/puppetlabs/puppet/bin/ruby': double free or corruption (fasttop): 0x000000000273fc00 ***

Root cause

Inside the XML file, I see the following Realm elements:

    <Realm><Realm className="org.apache.catalina.realm.JNDIRealm"></Realm>
</Realm>
<Realm><Realm connectionURL="ldap://localhost"></Realm>
</Realm>

It seems the parser has mapped the exact same memory for elements of the struct tree member being passed to tree_unlink_raw() which makes sense because the elements are nested

Testcase

assuming testcase.xml present on system at /vagrant/testcase.xml and with a freshly compiled libaugeas from git, free of any source modifications:

[root@localhost vagrant]# augtool
augtool> set /augeas/load/Xfm/lens Xml.lns
augtool> set /augeas/load/Xfm/incl /vagrant/testcase.xml
augtool> load
augtool> rm /files/vagrant/testcase.xml/Server//Realm
*** Error in `augtool': double free or corruption (fasttop): 0x00000000038790d0 ***
======= Backtrace: =========
/lib64/libc.so.6(+0x7d56d)[0x7f288a46e56d]
/usr/local/lib/libaugeas.so.0(+0x63f9)[0x7f288af7a3f9]
/usr/local/lib/libaugeas.so.0(+0x7c16)[0x7f288af7bc16]
/usr/local/lib/libaugeas.so.0(+0x7c07)[0x7f288af7bc07]
/usr/local/lib/libaugeas.so.0(+0x7cb5)[0x7f288af7bcb5]
/usr/local/lib/libaugeas.so.0(+0x8572)[0x7f288af7c572]
/usr/local/lib/libaugeas.so.0(aug_rm+0x43)[0x7f288af7c5f3]
/usr/local/lib/libaugeas.so.0(+0xadaa)[0x7f288af7edaa]
/usr/local/lib/libaugeas.so.0(aug_srun+0x2c2)[0x7f288af80412]
augtool[0x402a0f]
augtool[0x402d24]
augtool[0x401f5d]
/lib64/libc.so.6(__libc_start_main+0xf5)[0x7f288a412af5]
augtool[0x402131]
======= Memory map: ========
00400000-00405000 r-xp 00000000 fd:01 101854467                          /usr/local/bin/augtool
00604000-00605000 r--p 00004000 fd:01 101854467                          /usr/local/bin/augtool
00605000-00606000 rw-p 00005000 fd:01 101854467                          /usr/local/bin/augtool
01b4b000-07b57000 rw-p 00000000 00:00 0                                  [heap]
7f2882a5e000-7f2882a69000 r-xp 00000000 fd:01 67591521                   /usr/lib64/libnss_files-2.17.so
7f2882a69000-7f2882c68000 ---p 0000b000 fd:01 67591521                   /usr/lib64/libnss_files-2.17.so
7f2882c68000-7f2882c69000 r--p 0000a000 fd:01 67591521                   /usr/lib64/libnss_files-2.17.so
7f2882c69000-7f2882c6a000 rw-p 0000b000 fd:01 67591521                   /usr/lib64/libnss_files-2.17.so
7f2882c6a000-7f2882d68000 rw-p 00000000 00:00 0
7f2882d89000-7f2882f2d000 rw-p 00000000 00:00 0
7f2882f2d000-7f2889454000 r--p 00000000 fd:01 67591550                   /usr/lib/locale/locale-archive
7f2889454000-7f288946a000 r-xp 00000000 fd:01 67591529                   /usr/lib64/libpthread-2.17.so
7f288946a000-7f288966a000 ---p 00016000 fd:01 67591529                   /usr/lib64/libpthread-2.17.so
7f288966a000-7f288966b000 r--p 00016000 fd:01 67591529                   /usr/lib64/libpthread-2.17.so
7f288966b000-7f288966c000 rw-p 00017000 fd:01 67591529                   /usr/lib64/libpthread-2.17.so
7f288966c000-7f2889670000 rw-p 00000000 00:00 0
7f2889670000-7f2889771000 r-xp 00000000 fd:01 67591511                   /usr/lib64/libm-2.17.so
7f2889771000-7f2889970000 ---p 00101000 fd:01 67591511                   /usr/lib64/libm-2.17.so
7f2889970000-7f2889971000 r--p 00100000 fd:01 67591511                   /usr/lib64/libm-2.17.so
7f2889971000-7f2889972000 rw-p 00101000 fd:01 67591511                   /usr/lib64/libm-2.17.so
7f2889972000-7f2889996000 r-xp 00000000 fd:01 68828937                   /usr/lib64/liblzma.so.5.0.99
7f2889996000-7f2889b95000 ---p 00024000 fd:01 68828937                   /usr/lib64/liblzma.so.5.0.99
7f2889b95000-7f2889b96000 r--p 00023000 fd:01 68828937                   /usr/lib64/liblzma.so.5.0.99
7f2889b96000-7f2889b97000 rw-p 00024000 fd:01 68828937                   /usr/lib64/liblzma.so.5.0.99
7f2889b97000-7f2889bac000 r-xp 00000000 fd:01 67177068                   /usr/lib64/libz.so.1.2.7
7f2889bac000-7f2889dab000 ---p 00015000 fd:01 67177068                   /usr/lib64/libz.so.1.2.7
7f2889dab000-7f2889dac000 r--p 00014000 fd:01 67177068                   /usr/lib64/libz.so.1.2.7
7f2889dac000-7f2889dad000 rw-p 00015000 fd:01 67177068                   /usr/lib64/libz.so.1.2.7
7f2889dad000-7f2889db0000 r-xp 00000000 fd:01 67591509                   /usr/lib64/libdl-2.17.so
7f2889db0000-7f2889faf000 ---p 00003000 fd:01 67591509                   /usr/lib64/libdl-2.17.so
7f2889faf000-7f2889fb0000 r--p 00002000 fd:01 67591509                   /usr/lib64/libdl-2.17.so
7f2889fb0000-7f2889fb1000 rw-p 00003000 fd:01 67591509                   /usr/lib64/libdl-2.17.so
7f2889fb1000-7f2889fd6000 r-xp 00000000 fd:01 67176957                   /usr/lib64/libtinfo.so.5.9
7f2889fd6000-7f288a1d6000 ---p 00025000 fd:01 67176957                   /usr/lib64/libtinfo.so.5.9
7f288a1d6000-7f288a1da000 r--p 00025000 fd:01 67176957                   /usr/lib64/libtinfo.so.5.9
7f288a1da000-7f288a1db000 rw-p 00029000 fd:01 67176957                   /usr/lib64/libtinfo.so.5.9
7f288a1db000-7f288a1f0000 r-xp 00000000 fd:01 68621177                   /usr/lib64/libgcc_s-4.8.3-20140911.so.1
7f288a1f0000-7f288a3ef000 ---p 00015000 fd:01 68621177                   /usr/lib64/libgcc_s-4.8.3-20140911.so.1
7f288a3ef000-7f288a3f0000 r--p 00014000 fd:01 68621177                   /usr/lib64/libgcc_s-4.8.3-20140911.so.1
7f288a3f0000-7f288a3f1000 rw-p 00015000 fd:01 68621177                   /usr/lib64/libgcc_s-4.8.3-20140911.so.1
7f288a3f1000-7f288a5a7000 r-xp 00000000 fd:01 67591503                   /usr/lib64/libc-2.17.so
7f288a5a7000-7f288a7a7000 ---p 001b6000 fd:01 67591503                   /usr/lib64/libc-2.17.so
7f288a7a7000-7f288a7ab000 r--p 001b6000 fd:01 67591503                   /usr/lib64/libc-2.17.so
7f288a7ab000-7f288a7ad000 rw-p 001ba000 fd:01 67591503                   /usr/lib64/libc-2.17.so
7f288a7ad000-7f288a7b2000 rw-p 00000000 00:00 0
7f288a7b2000-7f288a910000 r-xp 00000000 fd:01 68828939                   /usr/lib64/libxml2.so.2.9.1
7f288a910000-7f288ab0f000 ---p 0015e000 fd:01 68828939                   /usr/lib64/libxml2.so.2.9.1
7f288ab0f000-7f288ab17000 r--p 0015d000 fd:01 68828939                   /usr/lib64/libxml2.so.2.9.1
7f288ab17000-7f288ab19000 rw-p 00165000 fd:01 68828939                   /usr/lib64/libxml2.so.2.9.1
7f288ab19000-7f288ab1b000 rw-p 00000000 00:00 0
7f288ab1b000-7f288ab57000 r-xp 00000000 fd:01 67177123                   /usr/lib64/libreadline.so.6.2
7f288ab57000-7f288ad57000 ---p 0003c000 fd:01 67177123                   /usr/lib64/libreadline.so.6.2
7f288ad57000-7f288ad59000 r--p 0003c000 fd:01 67177123                   /usr/lib64/libreadline.so.6.2
7f288ad59000-7f288ad5f000 rw-p 0003e000 fd:01 67177123                   /usr/lib64/libreadline.so.6.2
7f288ad5f000-7f288ad61000 rw-p 00000000 00:00 0
7f288ad61000-7f288ad73000 r-xp 00000000 fd:01 101261092                  /usr/local/lib/libfa.so.1.4.1
7f288ad73000-7f288af72000 ---p 00012000 fd:01 101261092                  /usr/local/lib/libfa.so.1.4.1
7f288af72000-7f288af73000 r--p 00011000 fd:01 101261092                  /usr/local/lib/libfa.so.1.4.1
7f288af73000-7f288af74000 rw-p 00012000 fd:01 101261092                  /usr/local/lib/libfa.so.1.4.1
7f288af74000-7f288afbd000 r-xp 00000000 fd:01 101844511                  /usr/local/lib/libaugeas.so.0.20.0Aborted

In the second testcase, I've un-nested the elements and Augeas is able to remove them with no problems:

[root@localhost vagrant]# augtool
augtool> set /augeas/load/Xfm/lens Xml.lns
augtool> set /augeas/load/Xfm/incl /vagrant/testcase2.xml
augtool> load
augtool> rm /files/vagrant/testcase2.xml/Server//Realm
rm : /files/vagrant/testcase2.xml/Server//Realm 8

How to fix this?

I'm not immediately clear on how this can be fixed being new to the internals of Augeas.

Looking at the nesting of the elements I'm trying to delete, the pointer structure I'm seeing makes sense since its the exact same XML nodes inside one another. I had been working under the assumption that the code was copying an object by reference instead of cloning it but clearly the parsing code is correct and not at fault.

It looks like the trees passed for deletion in tree_rm() need to be de-duplicated somehow, so that we don't try to delete nodes that are contained within a parent tree that has already been deleted - but I'm not sure of the best way to do this, can anyone help?

Thanks!
testcase.xml.txt
testcase2.xml.txt

lutter added a commit to lutter/augeas that referenced this issue Nov 24, 2015
In a tree like /files/1/2, when we execute 'rm /files//*', the path
expression matches /files/1 and /files/1/2. When tree_rm goes to delete
these two nodes, it first deletes (frees) /files/1 and all its
descendents. By the time we try to delete /files/1/2, the pointer we have
to that is no longer valid and we end up causing a double-free.

With this change, we make sure we only delete a node if none of its
ancestors is being deleted beforehand in the same operation - deleting a
node, and one of its ancestors afterwards is fine as the pointer to the
ancestor is still valid.

Fixes hercules-team#319

Special shoutout to Geoff Williams for finding, diagnosing and filing a
great bug report about this.
@lutter
Copy link
Member

lutter commented Nov 24, 2015

Thanks for the excellent and thorough bug report. Having that made fixing this bug really easy. I'll leave the PR open for a couple days to give you chance to test it - if you do, lmk if it really does fix the issue.

@GeoffWilliams
Copy link
Contributor Author

NP! PR fixes issue

lutter added a commit to lutter/augeas that referenced this issue Nov 26, 2015
In a tree like /files/1/2, when we execute 'rm /files//*', the path
expression matches /files/1 and /files/1/2. When tree_rm goes to delete
these two nodes, it first deletes (frees) /files/1 and all its
descendents. By the time we try to delete /files/1/2, the pointer we have
to that is no longer valid and we end up causing a double-free.

With this change, we make sure we only delete a node if none of its
ancestors is being deleted beforehand in the same operation - deleting a
node, and one of its ancestors afterwards is fine as the pointer to the
ancestor is still valid.

Fixes hercules-team#319

Special shoutout to Geoff Williams for finding, diagnosing and filing a
great bug report about this.
xbezdick added a commit to xbezdick/openstack-puppet-modules that referenced this issue Dec 15, 2015
70a1b729140ee803724933edf839f5d7ae4daf39 Merge pull request redhat-openstack#113 from puppetlabs/1.3.x
e93da60ae443dc30763a463475edf6cd58eef7d8 Merge pull request redhat-openstack#111 from GeoffWilliams/augeas_segfault_workaround
6c5c77e7cf959abdd1ace4294bdb5728b9e27e06 Merge pull request redhat-openstack#110 from tphoney/release_1.3.3
259ed084aca3360d7dfc86e54072fb0e06b0cbfd 1.3.3 release prep
957dfdc5884a1ebe29dc296d8616339d20ffaae9 delete realm nodes in depth-first order to workaround augeas segfault (hercules-team/augeas#319)
501ad90d4f5391f79ec37f192a4dbc9ab4779e30 Merge pull request redhat-openstack#107 from joshbeard/purge_connector_readme
f277c2fa1f84f5e4c9325c7929e7f4024e558c14 Document 'purge_connectors'
cf4e55790aeefd8c558681d9d475e84a3b52b9cc Merge pull request redhat-openstack#105 from joshbeard/purge_connectors
ed4f2e98ab10256d5fad53468a9904e3be1ddff3 Merge pull request redhat-openstack#106 from DavidS/add-package-install-options
c6d42c40deca6025899df8579dd6cc61a6aac645 (MAINT) enable install options in package resource
23eeb156c63f8870c3b1ac398c9842dda33512ae Specify port when purging connectors
34d866bf2f455cf771262e147c5bd5722690e008 Merge pull request redhat-openstack#104 from puppetlabs/1.3.x

Change-Id: I084ab5906e10e01a9d1d6f488c4421b7bf5c4d3e
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants