From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Philipp Stephani
make_string > is > > a bug. So please use make_multibyte_string here instead. > > > > > > See above; my suggestion would be to change the string handling code by > > limiting encoding and decoding to a small set of functions where the > encoding > > would have to be specified explicitly. > > Once again, that's not the issue. AFAIU, you have already specified > (albeit implicitly) that all strings passed as arguments to and from > the module functions are UTF-8 encoded. And that's fine by me, that's > not what these comments are about. > > The first comment is about a minor issue in > module_copy_string_contents. Consider this fragment from that > function: > > ptrdiff_t raw_size = SBYTES (lisp_str); > > /* Emacs internal encoding is more-or-less UTF8, let's assume utf8 > encoded emacs string are the same byte size. */ > > if (!buffer || length == 0 || *length-1 < raw_size) > { > *length = raw_size + 1; > return false; > } > > Lisp_Object lisp_str_utf8 = ENCODE_UTF_8 (lisp_str); > eassert (raw_size == SBYTES (lisp_str_utf8)); > *length = raw_size + 1; > memcpy (buffer, SDATA (lisp_str_utf8), SBYTES (lisp_str_utf8)); > buffer[raw_size] = 0; > > The comment and the 'if' clause after it in effect tell that you want > to check whether 'buffer' has enough space to hold the encoded string, > but you don't know how many bytes will be needed for that. So you > make some assumption (which, as I pointed out, could be wrong in rare > cases), and reject the strings that fail your test. What I'm sating > is that after the call to ENCODE_UTF_8, you can compute the exact > length in bytes of the UTF-8 encoded string, so if you move the test > there, you won't have to make any assumptions and you won't err. > > The second comment is again about calling make_string (which > build_string calls internally): use make_multibyte_string instead. > > > static void module_set_user_finalizer (emacs_env *env, > > emacs_value uptr, > > emacs_finalizer_function fin) > > { > > check_main_thread (); > > eassert (module_non_local_exit_check (env) == > emacs_funcall_exit_return); > > const Lisp_Object lisp = value_to_lisp (uptr); > > if (! USER_PTRP (lisp)) module_wrong_type (env, Quser_ptr, lisp); > > XUSER_PTR (lisp)->finalizer = fin; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< > > } > > > > No validity checks on 'fin'? > > > > How should it be validated? In C an arbitrary (invalid) pointer could be > > passed. I think we just have to accept that this is UB. > > Given the extensive checks elsewhere in the file, this surprised me, > that's all. How to test it? calling valid_pointer_p would be one > thing I'd suggest. Maybe there are other (perhaps platform-dependent) > checks possible here, I would have to look around. For example, it > would be good to make sure this points into executable code. > You can't validate pointers in C. For example, is the following pointer valid? long a; double *p = (double *)(&a); The answer is no; this is an aliasing violation, and dereferencing p is UB, but you won't detect this by trying to read the process's address space. See also http://blogs.msdn.com/b/oldnewthing/archive/2006/09/27/773741.aspx. The best way to detect such issues is running under asan. > > > if (len > INT_MAX || len < envptr->min_arity || (envptr->max_arity > >= 0 && > > len > envptr->max_arity)) > > xsignal2 (Qwrong_number_of_arguments, module_format_fun_env (envptr), > > make_number (len)); > > > > Why the test against INT_MAX? EMACS_INT can legitimately be a 64-bit > > data type with values far exceeding INT_MAX. Why impose this > > limitation here? > > > > Because the nargs argument in the module interface is an int. > > Shouldn't it be EMACS_INT instead? > EMACS_INT is an internal type that isn't exposed to module authors. It should be either int or int64. > > > If you think functions with more than INT_MAX arguments should be > supported, > > the type for nargs should be changed to int64. > > I thought your design decision was already to use int64 for any > integer argument, no? > Daniel's initial design ( https://lists.gnu.org/archive/html/emacs-devel/2015-02/msg00960.html) had int, but I guess he wouldn't mind changing this to int64. > > Anyway, something that comes from Lisp should allow EMACS_INT, IMO. > It can't use EMACS_INT because that's an internal type defined in lisp.h, and emacs-module.h can't include lisp.h. Furthermore is should be a type with a static size to allow for ABI stability. Mismatches should be dealt with by explicit bounds checks. > > > allocate_emacs_value calls malloc; shouldn't it call xmalloc instead, > > or at least conform to the XMALLOC_BLOCK_INPUT_CHECK protocol? > > > > If xmalloc is called, then we need to make sure that no signals > (longjmps) can > > escape to module code. If appropriate setjmps are in place that should be > > doable, but I need to check whether there are edge cases. > > Paul already addressed that: xmalloc in Emacs does double or even > triple duty, and it would be a shame to lose that here. So I think we > should try to find a way to modify xmalloc such that it satisfies the > module code as well. > Could xmalloc grow a variant that is guaranteed not to call longjmp? > > > Btw, I wonder whether we should provide a more friendly capabilities > > for retrieving the function name and its module. dladdr is not > > portable enough, and we have all the information at our fingertips > > in the module's init function, we just need to keep it instead of > > throwing it away. I envision debugging module-related problems will > > not be a very rare situation, so we need any help we can get. WDYT? > > > > Hmm, I don't know whether we have access to the function name without > using > > dladdr. > > I thought about recording their names when you bind them to Lisp > symbols. > That would be nice, however it would require set_symbol_function, Fdefalias, etc. to explicitly check for module functions in their RHS. Not sure whether that's worth it. > > > About the tests: The Makefile in mod-test is Unix-specific: it uses a > > literal .so extension. I also think the Python script should be > > rewritten in Emacs Lisp, so that Python installation is not required. > > Finally, all of the module tests and associated files should be moved > > into test/, preferably even test/automated/ and made part of the > "make > > check" run. > > > > Yes, tracked in https://github.com/aaptel/emacs-dynamic-module/issues/34 > > You will see that I put there some preliminary stuff, so at least > these tests can be run manually on Windows, by overriding a single > Make variable from the command line. > > Thanks. > --089e0102def4f414030524fe4c0b Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
> From: Philipp Stephani <p.stephani2@gmail.com>
> Date: Thu, 19 Nov 2015 22:41:09 +0000
> Cc: emacs-dev= el@gnu.org
>
> Thanks for the thorough review; I'll prepare patches for all of th= ese issues.
Thank you.
>=C2=A0 =C2=A0 =C2=A0This comment in module.c:
>
>=C2=A0 =C2=A0 =C2=A0/* If checking is enabled, abort if the current thr= ead is not the
>=C2=A0 =C2=A0 =C2=A0Emacs main thread. */
>=C2=A0 =C2=A0 =C2=A0static void check_main_thread (void);
>
>=C2=A0 =C2=A0 =C2=A0confused me, because a later comment says:
>
>=C2=A0 =C2=A0 =C2=A0void module_init (void)
>=C2=A0 =C2=A0 =C2=A0{
>=C2=A0 =C2=A0 =C2=A0/* It is not guaranteed that dynamic initializers r= un in the main thread,
>=C2=A0 =C2=A0 =C2=A0therefore we detect the main thread here. */
>
>=C2=A0 =C2=A0 =C2=A0If dynamic initializers might run in a thread diffe= rent from the Emacs
>=C2=A0 =C2=A0 =C2=A0main thread, then the code in module_init will reco= rd that other
>=C2=A0 =C2=A0 =C2=A0thread, not the Emacs main thread, right?
>
> No, because module_init is guaranteed to be called from the main threa= d because
> main calls it explicitly.
So you are saying that module_init runs in the main thread, but
dynamic initializers might run in another thread?=C2=A0 How's that
possible?
>=C2=A0 =C2=A0 =C2=A0Also, another comment:
>
>=C2=A0 =C2=A0 =C2=A0/* On Windows, we store both a handle to the main t= hread and the
>=C2=A0 =C2=A0 =C2=A0thread ID because the latter can be reused when a t= hread
>=C2=A0 =C2=A0 =C2=A0terminates. */
>
>=C2=A0 =C2=A0 =C2=A0seems to imply that 'main_thread' here is n= ot the Emacs's main thread,
>=C2=A0 =C2=A0 =C2=A0because that thread never terminates as long as the= Emacs session is
>=C2=A0 =C2=A0 =C2=A0alive.
>
>=C2=A0 =C2=A0 =C2=A0So what's the deal here? what does this thread = checking supposed to
>=C2=A0 =C2=A0 =C2=A0detect?
>
> This guards against the Emacs main thread having exited while module c= ode in
> some other thread is still running and attempting to call Emacs functi= ons.
On what OS can this happen?=C2=A0 It cannot happen on Windows, AFAIK,http://blogs.msdn.com/b/oldnewthing/archiv= e/2010/08/27/10054832.aspx=C2=A0seems to indicate that it is possible t= hat other threads continue running after the main thread has exited.=C2=A0so
complicating the code by using a handle is not necessary on Windows,
because the thread ID of the main Emacs thread will never be reused as
long as the Emacs session is alive.=C2=A0 Moreover, we already have that
thread's ID in a global variable called dwMainThreadId, computed
during the session startup (albeit after module_init, so it would need
to be moved to an earlier stage), so we could simply compare against
its value in check_main_thread.My init= ial code didn't use a thread identifier at all because it could have be= en reused after the main thread ended, introducing a subtle bug.= =C2=A0
> This is undefined behavior, but we included an explicit check if
> checking is enabled because that case is somewhat subtle.
I'm surprised this could happen on some OS.=C2=A0 Can you point to any<= br> details about this?It's not ruled = out by the C standard or the APIs of operating systems. If Emacs doesn'= t call ExitThread or manipulates the C runtime, then indeed it can't ha= ppen (at least on Windows), but I think it's brittle to rely on such su= btleties without explicitly checking for them (somebody might introduce a c= all to ExitThread in the future without looking at module code).= =C2=A0
>=C2=A0 =C2=A0 =C2=A0In this code from module.c:
>
>=C2=A0 =C2=A0 =C2=A0Lisp_Object value =3D HASH_VALUE (h, i);
>=C2=A0 =C2=A0 =C2=A0eassert (NATNUMP (value));
>=C2=A0 =C2=A0 =C2=A0const EMACS_UINT refcount =3D XFASTINT (value);
>=C2=A0 =C2=A0 =C2=A0if (refcount >=3D MOST_POSITIVE_FIXNUM)
>=C2=A0 =C2=A0 =C2=A0{
>=C2=A0 =C2=A0 =C2=A0module_non_local_exit_signal_1 (env, Qoverflow_erro= r, Qnil);
>=C2=A0 =C2=A0 =C2=A0return NULL;
>=C2=A0 =C2=A0 =C2=A0}
>
>=C2=A0 =C2=A0 =C2=A0how can the 'if' clause ever be true? refco= unt is an Emacs integer,
>=C2=A0 =C2=A0 =C2=A0as you have just verified, no? And if this somehow = can happen, then
>=C2=A0 =C2=A0 =C2=A0why isn't there a similar check in the other fu= nctions?
>
> refcount can be MOST_POSITIVE_FIXNUM because that's an inclusive b= ound. It's
> important to check that case because later refcount is incremented by = one, and
> if it's equal to MOST_POSITIVE_FIXNUM it would be outside the allo= wed range
> afterwards. No other function increments numbers, thus no other functi= ons need
> this check.
OK, but then either there should have been a comment explaining this,
or, better, the test should have been after the addition.=C2=A0 (Which, by<= br> some strange luck -- or maybe something else -- is just what Paul
already did ;-)If the test gets moved = after the addition, then we should have a verify(MAX_EMACS_UINT - 1 > MO= ST_POSITIVE_FIXNUM) or use __builtin_add_overflow to make sure the addition= doesn't cause UB.=C2=A0
>=C2=A0 =C2=A0 =C2=A0Re this fragment from module.c:
>
>=C2=A0 =C2=A0 =C2=A0Lisp_Object ret =3D list4 (Qlambda,
>=C2=A0 =C2=A0 =C2=A0list2 (Qand_rest, Qargs),
>=C2=A0 =C2=A0 =C2=A0documentation ? build_string (documentation) : Qnil= ,
>=C2=A0 =C2=A0 =C2=A0list3 (module_call_func,
>=C2=A0 =C2=A0 =C2=A0envobj,
>=C2=A0 =C2=A0 =C2=A0Qargs));
>
>=C2=A0 =C2=A0 =C2=A0Thou shalt not use build_string, except when you _k= now_ the argument
>=C2=A0 =C2=A0 =C2=A0will always be a pure-ASCII string. Practically, th= is means the
>=C2=A0 =C2=A0 =C2=A0argument must be a constant ASCII string. See these= messages (and the
>=C2=A0 =C2=A0 =C2=A0preceding discussion, if you are interested) for th= e gory details:
>
>=C2=A0 =C2=A0 =C2=A0http://= lists.gnu.org/archive/html/bug-gnu-emacs/2013-10/msg00955.html
>=C2=A0 =C2=A0 =C2=A0http://= lists.gnu.org/archive/html/bug-gnu-emacs/2013-10/msg00976.html
>=C2=A0 =C2=A0 =C2=A0http://= lists.gnu.org/archive/html/bug-gnu-emacs/2013-10/msg00979.html
>
>=C2=A0 =C2=A0 =C2=A0The above should call make_multibyte_string instead= .
>
>
> We had a discussion about encodings in
> https://github.com/aaptel/emacs-dynamic= -module/issues/37. Sorry that this
> didn't get resolved earlier; it's an important point. My sugge= stion would be to
> always mandate specifying an encoding whenever a char* is passed, and = limit
> that to two or three functions dealing with creating strings and acces= sing
> string contents. Would that address your concerns?
No, this is not about encoding at all.=C2=A0 This is about calling
build_string: you should only call it when the argument is a fixed
ASCII string.=C2=A0 If the argument is not fixed or might include non-ASCII=
characters, you should call make_multibyte_string instead.=C2=A0 That's=
because build_string might decide on its own whether to produce a
unibyte or a multibyte string, out of your control, whereas we always
want a multibyte string in this context.
build_string doesn't encode or decode its argument, it creates a Lisp object whose text is taken from the argument.=C2=A0 It's similar to
make_number in that respect.I'll s= end a patch with my changes to explain my intention.=C2=A0=
>=C2=A0 =C2=A0 =C2=A0Again in module.c:
>
>=C2=A0 =C2=A0 =C2=A0/*
>=C2=A0 =C2=A0 =C2=A0* Emacs internal encoding is more-or-less UTF8, let= 's assume utf8
>=C2=A0 =C2=A0 =C2=A0* encoded emacs string are the same byte size.
>=C2=A0 =C2=A0 =C2=A0*/
>
>=C2=A0 =C2=A0 =C2=A0if (!buffer || length =3D=3D 0 || *length-1 < ra= w_size)
>=C2=A0 =C2=A0 =C2=A0{
>=C2=A0 =C2=A0 =C2=A0*length =3D raw_size + 1;
>=C2=A0 =C2=A0 =C2=A0return false;
>=C2=A0 =C2=A0 =C2=A0}
>
>=C2=A0 =C2=A0 =C2=A0I don't understand why you need this assumption= . You are going to
>=C2=A0 =C2=A0 =C2=A0encode the string in a moment, so why not test '= ;length' against the
>=C2=A0 =C2=A0 =C2=A0size you actually obtain there? (The above test wil= l misfire when the
>=C2=A0 =C2=A0 =C2=A0original string includes characters above the Unico= de codespace, which
>=C2=A0 =C2=A0 =C2=A0require 5 bytes internally, but their encoding maps= them to Unicode
>=C2=A0 =C2=A0 =C2=A0codepoints which cannot take more than 4 bytes. So = you might reject
>=C2=A0 =C2=A0 =C2=A0perfectly valid calls.)
>
>=C2=A0 =C2=A0 =C2=A0In module_make_string you have:
>
>=C2=A0 =C2=A0 =C2=A0/* Assume STR is utf8 encoded */
>=C2=A0 =C2=A0 =C2=A0return lisp_to_value (env, make_string (str, length= ));
>
>=C2=A0 =C2=A0 =C2=A0The discussion I pointed to above concluded that &l= t;quote>make_string is
>=C2=A0 =C2=A0 =C2=A0a bug</quote>. So please use make_multibyte_s= tring here instead.
>
>
> See above; my suggestion would be to change the string handling code b= y
> limiting encoding and decoding to a small set of functions where the e= ncoding
> would have to be specified explicitly.
Once again, that's not the issue.=C2=A0 AFAIU, you have already specifi= ed
(albeit implicitly) that all strings passed as arguments to and from
the module functions are UTF-8 encoded.=C2=A0 And that's fine by me, th= at's
not what these comments are about.
The first comment is about a minor issue in
module_copy_string_contents.=C2=A0 Consider this fragment from that
function:
=C2=A0 ptrdiff_t raw_size =3D SBYTES (lisp_str);
=C2=A0 /* Emacs internal encoding is more-or-less UTF8, let's assume ut= f8
=C2=A0 =C2=A0 =C2=A0encoded emacs string are the same byte size.=C2=A0 */
=C2=A0 if (!buffer || length =3D=3D 0 || *length-1 < raw_size)
=C2=A0 =C2=A0 {
=C2=A0 =C2=A0 =C2=A0 *length =3D raw_size + 1;
=C2=A0 =C2=A0 =C2=A0 return false;
=C2=A0 =C2=A0 }
=C2=A0 Lisp_Object lisp_str_utf8 =3D ENCODE_UTF_8 (lisp_str);
=C2=A0 eassert (raw_size =3D=3D SBYTES (lisp_str_utf8));
=C2=A0 *length =3D raw_size + 1;
=C2=A0 memcpy (buffer, SDATA (lisp_str_utf8), SBYTES (lisp_str_utf8));
=C2=A0 buffer[raw_size] =3D 0;
The comment and the 'if' clause after it in effect tell that you wa= nt
to check whether 'buffer' has enough space to hold the encoded stri= ng,
but you don't know how many bytes will be needed for that.=C2=A0 So you=
make some assumption (which, as I pointed out, could be wrong in rare
cases), and reject the strings that fail your test.=C2=A0 What I'm sati= ng
is that after the call to ENCODE_UTF_8, you can compute the exact
length in bytes of the UTF-8 encoded string, so if you move the test
there, you won't have to make any assumptions and you won't err.
The second comment is again about calling make_string (which
build_string calls internally): use make_multibyte_string instead.
>=C2=A0 =C2=A0 =C2=A0static void module_set_user_finalizer (emacs_env *e= nv,
>=C2=A0 =C2=A0 =C2=A0emacs_value uptr,
>=C2=A0 =C2=A0 =C2=A0emacs_finalizer_function fin)
>=C2=A0 =C2=A0 =C2=A0{
>=C2=A0 =C2=A0 =C2=A0check_main_thread ();
>=C2=A0 =C2=A0 =C2=A0eassert (module_non_local_exit_check (env) =3D=3D e= macs_funcall_exit_return);
>=C2=A0 =C2=A0 =C2=A0const Lisp_Object lisp =3D value_to_lisp (uptr);
>=C2=A0 =C2=A0 =C2=A0if (! USER_PTRP (lisp)) module_wrong_type (env, Qus= er_ptr, lisp);
>=C2=A0 =C2=A0 =C2=A0XUSER_PTR (lisp)->finalizer =3D fin; <<<= ;<<<<<<<<<<<<<<<<<<&l= t;<<<<<<<<<<<
>=C2=A0 =C2=A0 =C2=A0}
>
>=C2=A0 =C2=A0 =C2=A0No validity checks on 'fin'?
>
> How should it be validated? In C an arbitrary (invalid) pointer could = be
> passed. I think we just have to accept that this is UB.
Given the extensive checks elsewhere in the file, this surprised me,
that's all.=C2=A0 How to test it? calling valid_pointer_p would be one<= br> thing I'd suggest.=C2=A0 Maybe there are other (perhaps platform-depend= ent)
checks possible here, I would have to look around.=C2=A0 For example, it
would be good to make sure this points into executable code.You can't validate pointers in C. For example, is= the following pointer valid?long a;do= uble *p =3D (double *)(&a);The answer is no; = this is an aliasing violation, and dereferencing p is UB, but you won't= detect this by trying to read the process's address space. See also=C2= =A0http://blogs.msdn.com/b/oldnewthing/archive/2006/09/27/773741.aspx= a>. The best way to detect such issues is running under asan.=C2= =A0
>=C2=A0 =C2=A0 =C2=A0if (len > INT_MAX || len < envptr->min= _arity || (envptr->max_arity >=3D 0 &&
>=C2=A0 =C2=A0 =C2=A0len > envptr->max_arity))
>=C2=A0 =C2=A0 =C2=A0xsignal2 (Qwrong_number_of_arguments, module_format= _fun_env (envptr),
>=C2=A0 =C2=A0 =C2=A0make_number (len));
>
>=C2=A0 =C2=A0 =C2=A0Why the test against INT_MAX? EMACS_INT can legitim= ately be a 64-bit
>=C2=A0 =C2=A0 =C2=A0data type with values far exceeding INT_MAX. Why im= pose this
>=C2=A0 =C2=A0 =C2=A0limitation here?
>
> Because the nargs argument in the module interface is an int.
Shouldn't it be EMACS_INT instead?= EMACS_INT is an internal type that isn't exposed to module authors. It = should be either int or int64.=C2=A0
> If you think functions with more than INT_MAX arguments should be supp= orted,
> the type for nargs should be changed to int64.
I thought your design decision was already to use int64 for any
integer argument, no?Daniel's init= ial design (https://lists.gnu.org/archive/html/emacs-devel/2015-02/ms= g00960.html) had int, but I guess he wouldn't mind changing this to= int64.=C2=A0
Anyway, something that comes from Lisp should allow EMACS_INT, IMO.It can't use EMACS_INT because that's = an internal type defined in lisp.h, and emacs-module.h can't include li= sp.h. Furthermore is should be a type with a static size to allow for ABI s= tability. Mismatches should be dealt with by explicit bounds checks.<= div>=C2=A0
>=C2=A0 =C2=A0 =C2=A0allocate_emacs_value calls malloc; shouldn't it= call xmalloc instead,
>=C2=A0 =C2=A0 =C2=A0or at least conform to the XMALLOC_BLOCK_INPUT_CHEC= K protocol?
>
> If xmalloc is called, then we need to make sure that no signals (longj= mps) can
> escape to module code. If appropriate setjmps are in place that should= be
> doable, but I need to check whether there are edge cases.
Paul already addressed that: xmalloc in Emacs does double or even
triple duty, and it would be a shame to lose that here.=C2=A0 So I think we=
should try to find a way to modify xmalloc such that it satisfies the
module code as well.Could xmalloc grow= a variant that is guaranteed not to call longjmp?=C2=A0
>=C2=A0 =C2=A0 =C2=A0Btw, I wonder whether we should provide a more frie= ndly capabilities
>=C2=A0 =C2=A0 =C2=A0for retrieving the function name and its module. dl= addr is not
>=C2=A0 =C2=A0 =C2=A0portable enough, and we have all the information at= our fingertips
>=C2=A0 =C2=A0 =C2=A0in the module's init function, we just need to = keep it instead of
>=C2=A0 =C2=A0 =C2=A0throwing it away. I envision debugging module-relat= ed problems will
>=C2=A0 =C2=A0 =C2=A0not be a very rare situation, so we need any help w= e can get. WDYT?
>
> Hmm, I don't know whether we have access to the function name with= out using
> dladdr.
I thought about recording their names when you bind them to Lisp
symbols.
>=C2=A0 =C2=A0 =C2=A0About the tests: The Makefile in mod-test is Unix-s= pecific: it uses a
>=C2=A0 =C2=A0 =C2=A0literal .so extension. I also think the Python scri= pt should be
>=C2=A0 =C2=A0 =C2=A0rewritten in Emacs Lisp, so that Python installatio= n is not required.
>=C2=A0 =C2=A0 =C2=A0Finally, all of the module tests and associated fil= es should be moved
>=C2=A0 =C2=A0 =C2=A0into test/, preferably even test/automated/ and mad= e part of the "make
>=C2=A0 =C2=A0 =C2=A0check" run.
>
> Yes, tracked in https://github.com/aapt= el/emacs-dynamic-module/issues/34
You will see that I put there some preliminary stuff, so at least
these tests can be run manually on Windows, by overriding a single
Make variable from the command line.
Thanks.