From: Eli Zaretskii <eliz@gnu.org>
To: "Basil L. Contovounesios" <contovob@tcd.ie>
Cc: 34655@debbugs.gnu.org, p.stephani2@gmail.com
Subject: bug#34655: 26.1.92; Segfault in module with --module-assertions
Date: Mon, 18 Mar 2019 18:21:25 +0200 [thread overview]
Message-ID: <835zsgw3ui.fsf@gnu.org> (raw)
In-Reply-To: <87va0h12js.fsf@tcd.ie> (contovob@tcd.ie)
> From: "Basil L. Contovounesios" <contovob@tcd.ie>
> Cc: <34655@debbugs.gnu.org>, <p.stephani2@gmail.com>
> Date: Sun, 17 Mar 2019 23:52:55 +0000
>
> > I tried at the time to reproduce your problem, and failed. But I did
> > that on Windows, where I needed to replace the non-existent realpath
> > by an equivalent function, so it's not a faithful reproduction. I
> > will see if I can find time to look at this on a GNU machine, unless
> > someone beats me to it.
>
> Replacing 'canonicalize_file_name' with 'strdup' still reproduces the
> issue for me. Perhaps increasing the number of calls to
> realpath-truename from 1000 to 5000 will also help.
Right, the strdup part did that for me. (My previous attempt also
used strdup as part of the replacement, but still failed to reproduce.
Memory corruption bugs are frequently weird that way.)
So I modified your recipe slightly, like this:
(progn (module-load "/path/to/realpath.so")
(setq garbage-collection-messages t)
(dotimes (i 5000)
(message "%d" i)
(realpath-truename user-emacs-directory)))
put it on a file named rp.el, and then ran it under GDB:
(gdb) r -batch --module-assertions -l ./rp.el
Here's what I see:
[...]
3077
3078
3079
3080
Garbage collecting...
Garbage collecting...done
Thread 1 received signal SIGSEGV, Segmentation fault.
0x011e9918 in rpl_re_search_2 (bufp=0x17c0260 <searchbufs+4736>, str1=0x0,
size1=0, str2=0x0, size2=20, startpos=0, range=20, regs=0x0, stop=20)
at regex-emacs.c:3322
3322 buf_ch = STRING_CHAR_AND_LENGTH (d, buf_charlen)
Looks like it indeed crashes after a GC, and on my system needs more
than 3000 iterations. So let's run it with a breakpoint at the
beginning of GC:
(gdb) break alloc.c:6044
Breakpoint 3 at 0x11fa1bb: file alloc.c, line 6044.
(gdb) r -batch --module-assertions -l ./rp.el
[...]
3080
Thread 1 hit Breakpoint 3, garbage_collect_1 (gcst=0x82bb84) at alloc.c:6044
6044 record_in_backtrace (QAutomatic_GC, 0, 0);
The backtrace at this point:
(gdb) bt
#0 garbage_collect_1 (gcst=0x82bb84) at alloc.c:6044
#1 0x011fa88e in garbage_collect () at alloc.c:6241
#2 0x01149adc in maybe_gc () at lisp.h:5028
#3 0x0123b012 in Ffuncall (nargs=2, args=0x82bcb0) at eval.c:2829
#4 0x0128c3cf in module_funcall (env=0x6122730, fun=0x6122868, nargs=1,
args=0x82bd50) at emacs-module.c:483
#5 0x62d8136f in rp_funcall (env=0x6122730, value=0x82bd50,
name=0x62d83060 "directory-name-p", nargs=1, args=0x82bd50)
at realpath.c:62
#6 0x62d815cc in Frealpath_truename (env=0x6122730, nargs=1, args=0x82bd90,
data=0x0) at realpath.c:124
[...]
Lisp Backtrace:
"directory-name-p" (0x82bcb8)
"realpath-truename" (0x82bf80)
"while" (0x82c2c8)
"let" (0x82c538)
"eval-buffer" (0x82cab0)
"load-with-code-conversion" (0x82d0f0)
"load" (0x82d9b8)
"command-line-1" (0x82e3d0)
"command-line" (0x82efe8)
"normal-top-level" (0x82f690)
As you see, the call to Ffuncall is the one that triggers GC from time
to time.
What happens with our 'file' at this point?
(gdb) fr 6
(gdb) p file
$1 = (emacs_value) 0x6122848
(gdb) p *file
$2 = <incomplete type>
(gdb) p *(Lisp_Object *)file
$3 = XIL(0x8000000006121ed0)
(gdb) xtype
Lisp_String
(gdb) xstring
$4 = (struct Lisp_String *) 0x6121ed0
"d:/usr/eli/.emacs.d/"
Still valid. Now let's see who thrashes it:
(gdb) p *$4
$5 = {
u = {
s = {
size = 20,
size_byte = 20,
intervals = 0x0,
data = 0x611e9fc "d:/usr/eli/.emacs.d/"
},
next = 0x14,
gcaligned = 20 '\024'
}
}
(gdb) watch -l $4->u.s.data
Hardware watchpoint 4: -location $4->u.s.data
(gdb) c
Continuing.
Garbage collecting...
Thread 1 hit Hardware watchpoint 4: -location $4->u.s.data
Old value = (unsigned char *) 0x611e9fc "\024"
New value = (unsigned char *) 0x0
sweep_strings () at alloc.c:2163
2163 NEXT_FREE_LISP_STRING (s) = string_free_list;
(gdb) list
2158 /* Reset the strings's `data' member so that we
2159 know it's free. */
2160 s->u.s.data = NULL;
2161
2162 /* Put the string on the free-list. */
2163 NEXT_FREE_LISP_STRING (s) = string_free_list;
2164 string_free_list = ptr_bounds_clip (s, sizeof *s);
2165 ++nfree;
2166 }
2167 }
Bingo! This is sweep_strings freeing our string, because it evidently
doesn't think it's a Lisp object that is being still referenced.
The culprit is this fragment from emacs-module.c, which is called each
time you receive a Lisp object from Emacs which you want to use in
your module:
/* Convert O to an emacs_value. Allocate storage if needed; this can
signal if memory is exhausted. Must be an injective function. */
static emacs_value
lisp_to_value (emacs_env *env, Lisp_Object o)
{
if (module_assertions)
{
/* Add the new value to the list of values allocated from this
environment. The value is actually a pointer to the
Lisp_Object cast to emacs_value. We make a copy of the
object on the free store to guarantee unique addresses. */
ATTRIBUTE_MAY_ALIAS Lisp_Object *optr = xmalloc (sizeof o);
*optr = o;
void *vptr = optr;
ATTRIBUTE_MAY_ALIAS emacs_value ret = vptr;
struct emacs_env_private *priv = env->private_members;
priv->values = Fcons (make_mint_ptr (ret), priv->values);
return ret;
}
What this does is make a copy of each Lisp object you get from Emacs,
store that copy in memory allocated off the heap, and hand your module
a pointer to the copy instead of the original object. So when you
call, e.g., directory-name-p, an Emacs function, it gets that copy of
the object. But memory allocation by xmalloc doesn't record the
allocated memory in the red-black tree we maintain for the purposes of
detecting Lisp objects referenced by C stack-based variables. So when
GC comes to examine the C stack, it doesn't consider the variable
'file' in your module as being a pointer to a live Lisp object, and so
it doesn't mark it. Then the sweep phase of GC recycles your Lisp
object, which in this case involves setting the string's data to a
NULL pointer.
The patch to fix this is below; it simply marks these copied values by
hand, thus preventing them from being GCed. It ran successfully with
even 50,000 iterations.
Philipp, any comments/objections?
--- src/emacs-module.c~0 2019-02-25 10:18:35.000000000 +0200
+++ src/emacs-module.c 2019-03-18 08:33:00.564476000 +0200
@@ -1133,6 +1133,15 @@ mark_modules (void)
mark_object (priv->non_local_exit_symbol);
mark_object (priv->non_local_exit_data);
mark_object (priv->values);
+ if (module_assertions)
+ {
+ for (Lisp_Object values = priv->values;
+ CONSP (values); values = XCDR (values))
+ {
+ Lisp_Object *p = xmint_pointer (XCAR (values));
+ mark_object (*p);
+ }
+ }
}
}
next prev parent reply other threads:[~2019-03-18 16:21 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-02-25 21:00 bug#34655: 26.1.92; Segfault in module with --module-assertions Basil L. Contovounesios
2019-02-26 2:59 ` Richard Stallman
2019-02-26 11:16 ` Basil L. Contovounesios
2019-02-26 15:27 ` Eli Zaretskii
2019-02-26 18:42 ` Basil L. Contovounesios
2019-02-27 4:10 ` Richard Stallman
2019-02-26 15:45 ` Eli Zaretskii
2019-03-17 16:38 ` Basil L. Contovounesios
2019-03-17 17:08 ` Eli Zaretskii
2019-03-17 23:52 ` Basil L. Contovounesios
2019-03-18 16:21 ` Eli Zaretskii [this message]
2019-03-18 16:58 ` Basil L. Contovounesios
2019-03-18 17:53 ` Eli Zaretskii
2019-03-21 16:11 ` Philipp Stephani
2019-03-21 17:00 ` Eli Zaretskii
2019-03-21 18:28 ` Philipp Stephani
2019-03-21 19:23 ` Philipp Stephani
2019-03-21 19:34 ` Eli Zaretskii
2019-03-21 21:29 ` Basil L. Contovounesios
2019-03-22 7:11 ` Eli Zaretskii
2019-03-21 19:27 ` Eli Zaretskii
2019-03-21 19:37 ` Philipp Stephani
2019-03-21 19:50 ` Eli Zaretskii
2019-03-21 20:01 ` Philipp Stephani
2019-03-21 20:14 ` Eli Zaretskii
2019-03-21 20:26 ` Philipp Stephani
2019-03-21 20:44 ` Eli Zaretskii
2019-03-21 20:48 ` Daniel Colascione
2019-03-22 8:17 ` Eli Zaretskii
2019-03-21 21:31 ` Basil L. Contovounesios
2019-03-22 0:56 ` Stefan Monnier
2019-03-22 8:16 ` Eli Zaretskii
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=835zsgw3ui.fsf@gnu.org \
--to=eliz@gnu.org \
--cc=34655@debbugs.gnu.org \
--cc=contovob@tcd.ie \
--cc=p.stephani2@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).