Re: Copyovering on SIG..., +mprotect

From: Chris Jacobson (fear@ATHENET.NET)
Date: 05/02/98


On 5/1/98 6:57 PM, James Turner (turnerjh@XTN.NET) stated:

>> For dynamic memory allocation, the Electric Fence library by Bruce Perens
>> does exactly that - if you have a Redhat system, just adding -lefence to
>> the libs will link it in, replacing malloc,calloc and free. man efence.
>
>I used efence once, but it didn't seem to like my code very much -- it
>has _tremendous_ overhead.  I assume it adds one page before and after
>every allocation... 8k overhead per malloc.  My code uses about 12000
>mallocs in booting, not counting what happens when players log
>on... that's 96meg overhead!
>
>The guarded memory stuff I posted has the advantage of only having
>horrid overhead when you explicitly choose to use it :)

I re-wrote parts of the stl.llist.h LList<> template, to use protected
memory on the nodes.  It boiled down to protecting the nodes during add,
prepend, append, insertafter, insertbefore, with an unprotection to any
node requiring editing (at most, one node, and only in Append and the
Insert functions), which would be re-protected.

I used two macros:

#define PROTECT(a) mprotect(a, sizeof(*a), PROT_READ)
#define UNPROTECT(a) mprotect(a, sizeof(*a), PROT_READ | PROT_WRITE)

mprotect returns a result, so you might want to check it...
Also, mprotect can be called on any memory aquired by a memory allocation
function: the sample within the mprotect man pages shows it being used on
malloc'd pieces of memory.

While the code ran well and added the nodes and such, I never did any
error-checking as to whether it actually protected properly, as I assumed
it did... (but you know what they say about assume :-P)

Anyways, it ran well - but only up to a point.  At the dynamic boards
patch, it would crash in parse_message, at:


        char *buf = get_buffer(MAX_STRING_LENGTH);

        CREATE(tmsg, struct board_msg_info, 1);   <<< CRASH LINE

        MESG_ID(tmsg) = id;


My CREATE is defined as:

#define CREATE(result, type, number)  do {\
        if ((number * sizeof(type)) <= 0) {\
        mudlogf(BRF, 102, TRUE, "CODEERR: Attempt to alloc 0 at %s:%d",
__FUNCTION__, __LINE__);\
        }\
        if (!((result) = (type *) ALLOC ((number ? number : 1), sizeof(type))))\
                { perror("malloc failure"); abort(); } } while(0)

ALLOC is defined as:
#define ALLOC(a, b)             calloc(a, b)


Now what is odd about this is that the code preprocesses to:

do {
        if (20 <= 0) {
         mudlogf(BRF, 102, TRUE, "CODEERR: Attempt to alloc 0 at %s:%d",
"parse_message", 234);
        }
        if (!(tmsg = (struct board_msg_info *) calloc((1 ? 1 : 1), 20)))
                { perror("malloc failure"); abort(); } } while(0)

So there are no variables used (except tmsg, which is simply assigned).
And it does not crash EVERY time - only on a certain call to
parse_message.
The crash only occurs with the stl.llist.h that uses mprotect().
The GDB output says it is a crash in __lib_malloc(0), bt says it was
called by _DTOR_END_
(destructor cleanup?).  If GDB output is read from a corefile, arg1 (0)
is at an unreadable address, so that something screwy is happening.

It is odd, because it happens on the first call of parse_message() within
the 6th call of create_board().  Granted the boards patch is nowhere near
stock, it works fine otherwise.
I reproduced this crash many times.  Still haven't found a fix, so
PROTECT and UNPROTECT are null macros at the moment :-)

- Çhris Jacobson


     +------------------------------------------------------------+
     | Ensure that you have read the CircleMUD Mailing List FAQ:  |
     | http://democracy.queensu.ca/~fletcher/Circle/list-faq.html |
     +------------------------------------------------------------+



This archive was generated by hypermail 2b30 : 12/15/00 PST