This change does introduce an API change, and a somewhat possibly dangerous
structure size alteration, and seems to be out of bounds for apr 1.7.x
branch, as
identified in the commit message.

Anyone using a build crossing runtimes between apr 1.7.0 and 1.7.1 may
experience
unintended side-effects. The eventual change still is present for apr
1.8 adopters.

Thoughts? ylavic are you willing to revert?

On Thu, Mar 11, 2021 at 10:21 AM <yla...@apache.org> wrote:
>
> Author: ylavic
> Date: Thu Mar 11 16:21:07 2021
> New Revision: 1887497
>
> URL: http://svn.apache.org/viewvc?rev=1887497&view=rev
> Log:
> Merge r1887060 from trunk:
>
> Align apr_mmap()ing offset to a page boundary.  PR 65158.
>
> This is requirement for unix systems, and otherwise can cause failures or
> undefined behaviour with some filesystems.
>
> Add apr_mmap_t->poffset on unixes to track the offset to the previous page,
> relative to the user requested/actual offset. This allows ->mm and ->size to
> still point to the actual data, while munmap() now deletes the full range
> from the start of the page.
>
> Tests updated accordingly.
>
>
> * Changes between r1887060 and this 1.7.x backport
>
> Since the layout of struct apr_mmap_t cannot change (non opaque), the poffset
> is over-allocated in mmap/unix/mmap.cmmap.c which uses this layout internally:
>     struct mm_layout {
>         apr_mmap_t mm;
>         apr_off_t poffset;
>     };
>
> This works for all the apr_mmap_t created by apr_mmap_create() with which any
> apr_mmap_*() function can then be called.
>
> If a user creates an apr_mmap_t "by hand", with this change it cannot be 
> passed
> to apr_mmap_dup() without causing an out-of-bound read. This is hardly a new
> limitation though, the refcounting which is the point of apr_mmap_dup() and
> apr_mmap_delete() comes from the private function mmap_cleanup() anyway, so
> there is no point in using these two functions with hand made apr_mmap_t (if
> there ever was a point in the first place to create an hand made 
> apr_mmap_t..).
>
>
> Submitted by: ylavic
>
> Added:
>     apr/apr/branches/1.7.x/test/data/mmap_large_datafile.txt
>       - copied unchanged from r1887060, 
> apr/apr/trunk/test/data/mmap_large_datafile.txt
> Modified:
>     apr/apr/branches/1.7.x/   (props changed)
>     apr/apr/branches/1.7.x/CHANGES
>     apr/apr/branches/1.7.x/mmap/unix/mmap.c
>     apr/apr/branches/1.7.x/test/testfnmatch.c
>     apr/apr/branches/1.7.x/test/testmmap.c
>
> Propchange: apr/apr/branches/1.7.x/
> ------------------------------------------------------------------------------
>   Merged /apr/apr/trunk:r1887060
>
> Modified: apr/apr/branches/1.7.x/CHANGES
> URL: 
> http://svn.apache.org/viewvc/apr/apr/branches/1.7.x/CHANGES?rev=1887497&r1=1887496&r2=1887497&view=diff
> ==============================================================================
> --- apr/apr/branches/1.7.x/CHANGES [utf-8] (original)
> +++ apr/apr/branches/1.7.x/CHANGES [utf-8] Thu Mar 11 16:21:07 2021
> @@ -1,6 +1,8 @@
>                                                       -*- coding: utf-8 -*-
>  Changes for APR 1.7.1
>
> +  *) Align apr_mmap()ing offset to a page boundary.  PR 65158.  [Yann Ylavic]
> +
>    *) Don't silently set APR_FOPEN_NOCLEANUP for apr_file_mktemp() created 
> file
>       to avoid a fd and inode leak when/if later passed to 
> apr_file_setaside().
>       [Yann Ylavic]
>
> Modified: apr/apr/branches/1.7.x/mmap/unix/mmap.c
> URL: 
> http://svn.apache.org/viewvc/apr/apr/branches/1.7.x/mmap/unix/mmap.c?rev=1887497&r1=1887496&r2=1887497&view=diff
> ==============================================================================
> --- apr/apr/branches/1.7.x/mmap/unix/mmap.c (original)
> +++ apr/apr/branches/1.7.x/mmap/unix/mmap.c Thu Mar 11 16:21:07 2021
> @@ -14,6 +14,8 @@
>   * limitations under the License.
>   */
>
> +#include <assert.h>
> +
>  #include "apr.h"
>  #include "apr_private.h"
>  #include "apr_general.h"
> @@ -33,6 +35,9 @@
>  #if APR_HAVE_STDIO_H
>  #include <stdio.h>
>  #endif
> +#if APR_HAVE_UNISTD_H
> +#include <unistd.h>  /* for sysconf() */
> +#endif
>  #ifdef HAVE_SYS_STAT_H
>  #include <sys/stat.h>
>  #endif
> @@ -42,6 +47,34 @@
>
>  #if APR_HAS_MMAP || defined(BEOS)
>
> +#ifndef BEOS
> +struct mm_layout {
> +    apr_mmap_t mm;
> +    apr_off_t poffset;
> +};
> +
> +static APR_INLINE
> +apr_mmap_t *alloc_with_poffset(apr_pool_t *p)
> +{
> +    struct mm_layout *layout = apr_pcalloc(p, sizeof(*layout));
> +    return &layout->mm;
> +}
> +
> +static APR_INLINE
> +void set_poffset(apr_mmap_t *mm, apr_off_t poffset)
> +{
> +    struct mm_layout *layout = (struct mm_layout *)mm;
> +    layout->poffset = poffset;
> +}
> +
> +static APR_INLINE
> +apr_off_t get_poffset(apr_mmap_t *mm)
> +{
> +    struct mm_layout *layout = (struct mm_layout *)mm;
> +    return layout->poffset;
> +}
> +#endif
> +
>  static apr_status_t mmap_cleanup(void *themmap)
>  {
>      apr_mmap_t *mm = themmap;
> @@ -61,7 +94,7 @@ static apr_status_t mmap_cleanup(void *t
>  #ifdef BEOS
>      rv = delete_area(mm->area);
>  #else
> -    rv = munmap(mm->mm, mm->size);
> +    rv = munmap((char *)mm->mm - get_poffset(mm), mm->size + 
> get_poffset(mm));
>  #endif
>      mm->mm = (void *)-1;
>
> @@ -81,6 +114,8 @@ APR_DECLARE(apr_status_t) apr_mmap_creat
>      area_id aid = -1;
>      uint32 pages = 0;
>  #else
> +    static long psize;
> +    apr_off_t poffset = 0;
>      apr_int32_t native_flags = 0;
>  #endif
>
> @@ -97,9 +132,11 @@ APR_DECLARE(apr_status_t) apr_mmap_creat
>
>      if (file == NULL || file->filedes == -1 || file->buffered)
>          return APR_EBADF;
> -    (*new) = (apr_mmap_t *)apr_pcalloc(cont, sizeof(apr_mmap_t));
> -
> +
>  #ifdef BEOS
> +
> +    *new = (apr_mmap_t *)apr_pcalloc(cont, sizeof(apr_mmap_t));
> +
>      /* XXX: mmap shouldn't really change the seek offset */
>      apr_file_seek(file, APR_SET, &offset);
>
> @@ -121,7 +158,9 @@ APR_DECLARE(apr_status_t) apr_mmap_creat
>          read(file->filedes, mm, size);
>
>      (*new)->area = aid;
> +
>  #else
> +    *new = alloc_with_poffset(cont);
>
>      if (flag & APR_MMAP_WRITE) {
>          native_flags |= PROT_WRITE;
> @@ -130,7 +169,18 @@ APR_DECLARE(apr_status_t) apr_mmap_creat
>          native_flags |= PROT_READ;
>      }
>
> -    mm = mmap(NULL, size, native_flags, MAP_SHARED, file->filedes, offset);
> +#if defined(_SC_PAGESIZE)
> +    if (psize == 0) {
> +        psize = sysconf(_SC_PAGESIZE);
> +        /* the page size should be a power of two */
> +        assert(psize > 0 && (psize & (psize - 1)) == 0);
> +    }
> +    poffset = offset & (apr_off_t)(psize - 1);
> +#endif
> +
> +    mm = mmap(NULL, size + poffset,
> +              native_flags, MAP_SHARED,
> +              file->filedes, offset - poffset);
>
>      if (mm == (void *)-1) {
>          /* we failed to get an mmap'd file... */
> @@ -139,7 +189,8 @@ APR_DECLARE(apr_status_t) apr_mmap_creat
>      }
>  #endif
>
> -    (*new)->mm = mm;
> +    (*new)->mm = (char *)mm + poffset;
> +    set_poffset(*new, poffset);
>      (*new)->size = size;
>      (*new)->cntxt = cont;
>      APR_RING_ELEM_INIT(*new, link);
> @@ -154,7 +205,12 @@ APR_DECLARE(apr_status_t) apr_mmap_dup(a
>                                         apr_mmap_t *old_mmap,
>                                         apr_pool_t *p)
>  {
> +#ifdef BEOS
>      *new_mmap = (apr_mmap_t *)apr_pmemdup(p, old_mmap, sizeof(apr_mmap_t));
> +#else
> +    *new_mmap = (apr_mmap_t *)apr_pmemdup(p, old_mmap,
> +                                          sizeof(struct mm_layout));
> +#endif
>      (*new_mmap)->cntxt = p;
>
>      APR_RING_INSERT_AFTER(old_mmap, *new_mmap, link);
>
> Modified: apr/apr/branches/1.7.x/test/testfnmatch.c
> URL: 
> http://svn.apache.org/viewvc/apr/apr/branches/1.7.x/test/testfnmatch.c?rev=1887497&r1=1887496&r2=1887497&view=diff
> ==============================================================================
> --- apr/apr/branches/1.7.x/test/testfnmatch.c (original)
> +++ apr/apr/branches/1.7.x/test/testfnmatch.c Thu Mar 11 16:21:07 2021
> @@ -23,7 +23,7 @@
>   * .txt extension in the data directory at the time testfnmatch
>   * happens to be run (!?!). */
>
> -#define NUM_FILES (5)
> +#define NUM_FILES (6)
>
>  #define APR_FNM_BITS    15
>  #define APR_FNM_FAILBIT 256
>
> Modified: apr/apr/branches/1.7.x/test/testmmap.c
> URL: 
> http://svn.apache.org/viewvc/apr/apr/branches/1.7.x/test/testmmap.c?rev=1887497&r1=1887496&r2=1887497&view=diff
> ==============================================================================
> --- apr/apr/branches/1.7.x/test/testmmap.c (original)
> +++ apr/apr/branches/1.7.x/test/testmmap.c Thu Mar 11 16:21:07 2021
> @@ -35,18 +35,30 @@ static void not_implemented(abts_case *t
>
>  #else
>
> -static char test_string[256]; /* read from the datafile */
> +static apr_pool_t *ptest;
> +static char *thisfdata; /* read from the datafile */
>  static apr_mmap_t *themmap = NULL;
>  static apr_file_t *thefile = NULL;
>  static char *file1;
>  static apr_finfo_t thisfinfo;
>  static apr_size_t thisfsize;
>
> +static struct {
> +    const char *filename;
> +    apr_off_t offset;
> +} test_set[] = {
> +    { "/data/mmap_datafile.txt", 0 },
> +    { "/data/mmap_large_datafile.txt", 65536 },
> +    { "/data/mmap_large_datafile.txt", 66650 }, /* not page aligned */
> +    { NULL, }
> +};
> +
>  static void create_filename(abts_case *tc, void *data)
>  {
> +    const char *filename = data;
>      char *oldfileptr;
>
> -    apr_filepath_get(&file1, 0, p);
> +    apr_filepath_get(&file1, 0, ptest);
>  #ifndef NETWARE
>  #ifdef WIN32
>      ABTS_TRUE(tc, file1[1] == ':');
> @@ -57,7 +69,7 @@ static void create_filename(abts_case *t
>      ABTS_TRUE(tc, file1[strlen(file1) - 1] != '/');
>
>      oldfileptr = file1;
> -    file1 = apr_pstrcat(p, file1,"/data/mmap_datafile.txt" ,NULL);
> +    file1 = apr_pstrcat(ptest, file1, filename, NULL);
>      ABTS_TRUE(tc, oldfileptr != file1);
>  }
>
> @@ -67,24 +79,15 @@ static void test_file_close(abts_case *t
>
>      rv = apr_file_close(thefile);
>      ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
> +    thefile = NULL;
>  }
>
> -static void read_expected_contents(abts_case *tc, void *data)
> -{
> -    apr_status_t rv;
> -    apr_size_t nbytes = sizeof(test_string) - 1;
> -
> -    rv = apr_file_read(thefile, test_string, &nbytes);
> -    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
> -    test_string[nbytes] = '\0';
> -    thisfsize = strlen(test_string);
> -}
> -
>  static void test_file_open(abts_case *tc, void *data)
>  {
>      apr_status_t rv;
>
> -    rv = apr_file_open(&thefile, file1, APR_FOPEN_READ, APR_UREAD | 
> APR_GREAD, p);
> +    rv = apr_file_open(&thefile, file1, APR_FOPEN_READ,
> +                       APR_UREAD | APR_GREAD, ptest);
>      ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
>      ABTS_PTR_NOTNULL(tc, thefile);
>  }
> @@ -95,28 +98,54 @@ static void test_get_filesize(abts_case
>
>      rv = apr_file_info_get(&thisfinfo, APR_FINFO_NORM, thefile);
>      ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
> -    ABTS_ASSERT(tc, "File size mismatch", thisfsize == thisfinfo.size);
> +
> +    thisfsize = thisfinfo.size;
> +    thisfdata = apr_palloc(ptest, thisfsize + 1);
> +    ABTS_PTR_NOTNULL(tc, thisfdata);
> +}
> +
> +static void read_expected_contents(abts_case *tc, void *data)
> +{
> +    apr_off_t *offset = data;
> +    apr_size_t nbytes = 0;
> +    apr_status_t rv;
> +
> +    rv = apr_file_read_full(thefile, thisfdata, thisfsize, &nbytes);
> +    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
> +    ABTS_ASSERT(tc, "File size mismatch", nbytes == thisfsize);
> +    thisfdata[nbytes] = '\0';
> +    ABTS_ASSERT(tc, "File content size mismatch",
> +                strlen(thisfdata) == thisfsize);
> +    ABTS_ASSERT(tc, "File size too small",
> +                (apr_size_t)*offset < thisfsize);
> +
> +    /* From now, pretend that the file data and size don't include the
> +     * offset, this avoids adding/substrating it to thisfdata/thisfsize
> +     * all over the place in the next tests.
> +     */
> +    thisfdata += *offset;
> +    thisfsize -= *offset;
>  }
>
>  static void test_mmap_create(abts_case *tc, void *data)
>  {
> +    apr_off_t *offset = data;
>      apr_status_t rv;
>
> -    rv = apr_mmap_create(&themmap, thefile, 0, (apr_size_t) thisfinfo.size,
> -                                APR_MMAP_READ, p);
> +    rv = apr_mmap_create(&themmap, thefile, *offset, thisfsize,
> +                         APR_MMAP_READ, ptest);
>      ABTS_PTR_NOTNULL(tc, themmap);
>      ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
>  }
>
>  static void test_mmap_contents(abts_case *tc, void *data)
>  {
> -
>      ABTS_PTR_NOTNULL(tc, themmap);
>      ABTS_PTR_NOTNULL(tc, themmap->mm);
>      ABTS_SIZE_EQUAL(tc, thisfsize, themmap->size);
>
>      /* Must use nEquals since the string is not guaranteed to be NULL 
> terminated */
> -    ABTS_STR_NEQUAL(tc, themmap->mm, test_string, thisfsize);
> +    ABTS_STR_NEQUAL(tc, themmap->mm, thisfdata, thisfsize);
>  }
>
>  static void test_mmap_delete(abts_case *tc, void *data)
> @@ -126,6 +155,7 @@ static void test_mmap_delete(abts_case *
>      ABTS_PTR_NOTNULL(tc, themmap);
>      rv = apr_mmap_delete(themmap);
>      ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
> +    themmap = NULL;
>  }
>
>  static void test_mmap_offset(abts_case *tc, void *data)
> @@ -138,8 +168,9 @@ static void test_mmap_offset(abts_case *
>
>      ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
>      /* Must use nEquals since the string is not guaranteed to be NULL 
> terminated */
> -    ABTS_STR_NEQUAL(tc, addr, test_string + 5, thisfsize-5);
> +    ABTS_STR_NEQUAL(tc, addr, thisfdata + 5, thisfsize - 5);
>  }
> +
>  #endif
>
>  abts_suite *testmmap(abts_suite *suite)
> @@ -147,15 +178,21 @@ abts_suite *testmmap(abts_suite *suite)
>      suite = ADD_SUITE(suite)
>
>  #if APR_HAS_MMAP
> -    abts_run_test(suite, create_filename, NULL);
> -    abts_run_test(suite, test_file_open, NULL);
> -    abts_run_test(suite, read_expected_contents, NULL);
> -    abts_run_test(suite, test_get_filesize, NULL);
> -    abts_run_test(suite, test_mmap_create, NULL);
> -    abts_run_test(suite, test_mmap_contents, NULL);
> -    abts_run_test(suite, test_mmap_offset, NULL);
> -    abts_run_test(suite, test_mmap_delete, NULL);
> -    abts_run_test(suite, test_file_close, NULL);
> +    apr_size_t i;
> +    apr_pool_create(&ptest, p);
> +    for (i = 0; test_set[i].filename; ++i) {
> +        abts_run_test(suite, create_filename, (void *)test_set[i].filename);
> +        abts_run_test(suite, test_file_open, NULL);
> +        abts_run_test(suite, test_get_filesize, NULL);
> +        abts_run_test(suite, read_expected_contents, &test_set[i].offset);
> +        abts_run_test(suite, test_mmap_create, &test_set[i].offset);
> +        abts_run_test(suite, test_mmap_contents, &test_set[i].offset);
> +        abts_run_test(suite, test_mmap_offset, &test_set[i].offset);
> +        abts_run_test(suite, test_mmap_delete, NULL);
> +        abts_run_test(suite, test_file_close, NULL);
> +        apr_pool_clear(ptest);
> +    }
> +    apr_pool_destroy(ptest);
>  #else
>      abts_run_test(suite, not_implemented, NULL);
>  #endif
>
>

Reply via email to