head -r320521 (e.g.): another powerpc64 problem: programs using fgets crash trying to store address over code instead of into __cleanup_info__

From: Mark Millard <markmi_at_dsl-only.net>
Date: Sat, 1 Jul 2017 19:42:11 -0700
[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
Received on Sun Jul 02 2017 - 00:42:20 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:41:12 UTC