head -r320521 (e.g.): another powerpc64 problem: programs using fgets crash trying to store address over code instead of into __cleanup_info__
Mark Millard
markmi at dsl-only.net
Sun Jul 2 02:42:21 UTC 2017
[Note: this is from a amd64 -> powerpc64 cross-build based
on system clang 4 instead of gcc 4.2.1. I'm building a
gcc 4.2.1 based system currently so that I can test
a more standard configuration. But I'm one of the ones
that experiments with finding things to report for
clang targeting powerpc64 and powerpc.]
powerpc64 is having programs crash with an attempt
to store addresses over code instead of into
__cleanup_info__ when fgets is used. ntpd is an
example. As is sshd (although I've looked at
its details less).
Building up the context for this claim. . .
public declaration:
struct _pthread_cleanup_info {
__uintptr_t pthread_cleanup_pad[8];
};
private declaration:
struct pthread_cleanup {
struct pthread_cleanup *prev;
void (*routine)(void *);
void *routine_arg;
int onheap;
};
ntpd and sshd die with segmentation faults in:
void
__pthread_cleanup_push_imp(void (*routine)(void *), void *arg,
struct _pthread_cleanup_info *info)
{
struct pthread *curthread = _get_curthread();
struct pthread_cleanup *newbuf;
newbuf = (void *)info;
newbuf->routine = routine;
newbuf->routine_arg = arg;
newbuf->onheap = 0;
newbuf->prev = curthread->cleanup;
curthread->cleanup = newbuf;
}
at the statement: newbuf->routine = routine;
But it turns out that the bt is like:
__pthread_cleanup_push_imp(routine=0x507b1248 <__stdio_cancel_cleanup>, arg=0x0, info=0x509eaaf4)
__pthread_cleanup_push_imp_int(p0=0x507b1248,p1=0x0)
fgets (buf=0x51415000 "", n=511, fp=0x507d4c40)
. . .
Note the 2 arguments to __pthread_cleanup_push_imp_int when called
from fgets but the 3 arguemnts to __pthread_cleanup_push_imp . . .
fgets uses FLOCK_CANCELSAFE(fp) :
#define FLOCKFILE_CANCELSAFE(fp) \
{ \
struct _pthread_cleanup_info __cleanup_info__; \
if (__isthreaded) { \
_FLOCKFILE(fp); \
___pthread_cleanup_push_imp( \
__stdio_cancel_cleanup, (fp), \
&__cleanup_info__); \
} else { \
___pthread_cleanup_push_imp( \
__stdio_cancel_cleanup, NULL, \
&__cleanup_info__); \
} \
{
#define FUNLOCKFILE_CANCELSAFE() \
(void)0; \
} \
___pthread_cleanup_pop_imp(1); \
}
where here the NULL case is in use. 3 arguments.
But:
STUB_FUNC2(__pthread_cleanup_push_imp, PJT_CLEANUP_PUSH_IMP, void, void*, void *);
which is use of:
#define STUB_FUNC2(name, idx, ret, p0_type, p1_type) \
static ret FUNC_EXP(name)(p0_type, p1_type) __used; \
static ret FUNC_INT(name)(p0_type, p1_type) __used; \
WEAK_REF(FUNC_EXP(name), name); \
WEAK_REF(FUNC_INT(name), __CONCAT(_, name)); \
typedef ret (*FUNC_TYPE(name))(p0_type, p1_type); \
static ret FUNC_EXP(name)(p0_type p0, p1_type p1) \
{ \
FUNC_TYPE(name) func; \
func = (FUNC_TYPE(name))__thr_jtable[idx][0]; \
return (func(p0, p1)); \
} \
static ret FUNC_INT(name)(p0_type p0, p1_type p1) \
{ \
FUNC_TYPE(name) func; \
func = (FUNC_TYPE(name))__thr_jtable[idx][1]; \
return (func(p0, p1)); \
}
so only 2 arguments to func (i.e., __pthread_cleanup_push_imp
here).
Compared to:
___pthread_cleanup_push_imp( \
__stdio_cancel_cleanup, NULL, \
&__cleanup_info__); \
As a result junk is used instead of &__cleanup_info__.
On powerpc64 it happens to be the address of
___pthread_cleanup_push_imp that happens to be
in r5 (normally the third argument) at the time.
So:
newbuf->routine = routine;
tries to replace the first instruction of
___pthread_cleanup_push_imp .
As far as I can tell what should be used is:
#define STUB_FUNC3(name, idx, ret, p0_type, p1_type, p2_type) \
static ret FUNC_EXP(name)(p0_type, p1_type, p2_type) __used; \
static ret FUNC_INT(name)(p0_type, p1_type, p2_type) __used; \
WEAK_REF(FUNC_EXP(name), name); \
WEAK_REF(FUNC_INT(name), __CONCAT(_, name)); \
typedef ret (*FUNC_TYPE(name))(p0_type, p1_type, p2_type); \
static ret FUNC_EXP(name)(p0_type p0, p1_type p1, p2_type p2) \
{ \
FUNC_TYPE(name) func; \
func = (FUNC_TYPE(name))__thr_jtable[idx][0]; \
return (func(p0, p1, p2)); \
} \
static ret FUNC_INT(name)(p0_type p0, p1_type p1, p2_type p2) \
{ \
FUNC_TYPE(name) func; \
func = (FUNC_TYPE(name))__thr_jtable[idx][1]; \
return (func(p0, p1, p2)); \
}
with the p2_type being: struct _pthread_cleanup_info *
but I'm not expert in this code.
===
Mark Millard
markmi at dsl-only.net
More information about the freebsd-current
mailing list