Bug 86439 - memccpy() can read past end of source string
Summary: memccpy() can read past end of source string
Keywords:
Status: CLOSED ERRATA
Alias: None
Product: Red Hat Enterprise Linux 2.1
Classification: Red Hat
Component: glibc
Version: 2.1
Hardware: ia64
OS: Linux
medium
medium
Target Milestone: ---
Assignee: Jakub Jelinek
QA Contact: Brian Brock
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2003-03-21 23:35 UTC by Jason Uhlenkott
Modified: 2016-11-24 14:50 UTC (History)
1 user (show)

Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Clone Of:
Environment:
Last Closed: 2003-05-22 16:42:16 UTC
Target Upstream Version:
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Red Hat Product Errata RHSA-2003:090 0 high SHIPPED_LIVE Critical: glibc security update 2003-05-22 04:00:00 UTC

Description Jason Uhlenkott 2003-03-21 23:35:25 UTC
/*
The memccpy() call below will cause a segmentation fault on ia64
systems:  It tries to read beyond the end of srcbuf and hits an
unreadable page.  Is this a bug?

Note that we're deliberately indicating a size of SIZE+8 characters to
memccpy even though srcbuf is only SIZE characters long.

One point of view is that memccpy should never attempt to read more
than SIZE characters:  It should see the 0 character at srcbuf[SIZE-1]
and stop there.  memccpy() is supposed to stop after it copies n
characters or after it copies the character specified by the argument
c, whichever comes first, so obviously it's ok for the source string
to be shorter than n characters.

Another point of view is that just because it only copies SIZE
characters, that doesn't mean it will only try to access SIZE
characters from the source string.  The length of the source string is
allowed to be less than n characters, but the size of the object
containing the source string isn't.

The C standard contains some explicit language which comes down on the
side of the latter point of view.  Section 7.21.1 paragraph 2 (I'm
quoting from C99 since that's what I have handy, but C89/C90 has the
same requirements):
-----
| Where an argument declared as size_t n specifies the length of the
| array for a function, n can have the value zero on a call to that
| function.  Unless explicitly stated otherwise in the description of a
| particular function in this subclause, pointer arguments on such a
| call shall still have valid values, as described in 7.1.4.
-----
Section 7.1.4 states:
-----
| If a function argument is described as being an array, the pointer
| actually passed to the function shall have a value such that all
| address computations and accesses to objects (that would be valid if
| the pointer did point to the first element of such an array) are in
| fact valid.
-----
So if memccpy were a standard C function, we'd clearly be invoking
undefined behavior by doing what we're doing.  memccpy is not part of
the standard C library however.

One point which has caused some consternation is that these two
statements are expected to behave identically:
        memccpy(dstbuf, srcbuf, 0, SIZE+8);
        strncpy(dstbuf, srcbuf, SIZE+8);
Yet the memccpy call causes a segfault while the strncpy call does
not.
*/


#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <assert.h>

#define SIZE 32768

int main(void) {

        int fd;
        char *srcbuf;
        char dstbuf[2*SIZE];
        char *protected;

        fd = open("/dev/zero", O_RDONLY);
        if(fd == -1) {
                perror("open");
                exit(EXIT_FAILURE);
        }

        srcbuf = mmap(0, SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
        if(srcbuf == (char *)-1) {
                perror("mmap");
                exit(EXIT_FAILURE);
        }
        protected = mmap(srcbuf+SIZE, SIZE, PROT_NONE,
                MAP_FIXED|MAP_PRIVATE, fd, SIZE);
        if(protected == (char *)-1) {
                perror("mmap");
                exit(EXIT_FAILURE);
        }
        assert(protected == srcbuf+SIZE);

        memset(srcbuf, 'x', SIZE-1);
        srcbuf[SIZE-1]='\0';

#if 1
        memccpy(dstbuf, srcbuf, 0, SIZE+8);
#else
        strncpy(dstbuf, srcbuf, SIZE+8);
#endif

        return 0;
}

Comment 1 Mark J. Cox 2003-05-22 16:42:17 UTC
An errata has been issued which should help the problem described in this bug report. 
This report is therefore being closed with a resolution of ERRATA. For more information
on the solution and/or where to find the updated files, please follow the link below. You may reopen 
this bug report if the solution does not work for you.

http://rhn.redhat.com/errata/RHSA-2003-090.html



Note You need to log in before you can comment on or make changes to this bug.