On Tue, Jun 25, 2019 at 09:06:42AM +0000, Plüm, Rüdiger, Vodafone Group wrote:
> > On Tue, Jun 25, 2019 at 08:49:08AM +0100, Joe Orton wrote:
> > > Unless I am missing something the apr_dir_open/apr_dir_read API design
> > > is always going to have memory use proportional to directory size
> > > because apr_dir_read() duplicates the filename into the directory's
> > > pool.  We need an apr_dir_pread() or whatever which takes a pool
> > > argument. :(
> > 
> > OK, yes, this plus an iterpool in dav_fs_walker() fixes it.
> 
> Only the iterpool in dav_fs_walker or plus the changes in APR (apr_dir_pread)?

Yes, both - PoC attached but I'm not at all confident this is safe, esp 
in the recursion case.

The test I was running before was only a "core" liveprop provided by 
mod_dav itself, so I also tested the deadprops case and that still seems 
to leak.  sbrk() to increase the heap is getting triggered by 
apr_sdbm_open() every time I saw it, but apr_dbm_open() is being pass 
the scratchpool so I think it's not the cause.

Running "dump_all_pools propdb->p" at this point gives me:

   Pool 'transaction' [0x24e1d88]: 7264/8192 free (1 blocks) allocator: 
0x24d2b90 free blocks in allocator: 0 kiB
     Pool 'master_conn' [0x2504e28]: 1224/8192 free (1 blocks) allocator: 
0x24d2b90 free blocks in allocator: 0 kiB
       Pool 'deferred_pool' [0x263ef18]: 8032/8192 free (1 blocks) allocator: 
0x24d2b90 free blocks in allocator: 0 kiB
         Pool 'request' [0x2506e38]: 13672/1646592 free (201 blocks) allocator: 
0x24d2b90 free blocks in allocator: 0 kiB
           Pool 'mod_dav_fs-walker' [0x2524648]: 8016/8192 free (1 blocks) 
allocator: 0x24d2b90 free blocks in allocator: 0 kiB
           Pool 'mod_dav-scratch' [0x2508e48]: 7224/8192 free (1 blocks) 
allocator: 0x24d2b90 free blocks in allocator: 0 kiB
           Pool 'mod_lua-vm' [0x250ce68]: 6360/16384 free (2 blocks) allocator: 
0x24d2b90 free blocks in allocator: 0 kiB

[trimmed some whitespace only]

I think this is implicating misuse of r->pool, that's grown rather 
large?

Regards, Joe

Index: include/apr_file_info.h
===================================================================
--- include/apr_file_info.h     (revision 1862036)
+++ include/apr_file_info.h     (working copy)
@@ -268,6 +268,24 @@
                                        apr_dir_t *thedir);
 
 /**
+ * Read the next entry from the specified directory. 
+ * @param finfo the file info structure and filled in by apr_dir_read
+ * @param wanted The desired apr_finfo_t fields, as a bit flag of APR_FINFO_
+                 values 
+ * @param thedir the directory descriptor returned from apr_dir_open
+ * @param pool the pool to use for allocations
+ * @remark No ordering is guaranteed for the entries read.
+ *
+ * @note If @c APR_INCOMPLETE is returned all the fields in @a finfo may
+ *       not be filled in, and you need to check the @c finfo->valid bitmask
+ *       to verify that what you're looking for is there. When no more
+ *       entries are available, APR_ENOENT is returned.
+ */                        
+APR_DECLARE(apr_status_t) apr_dir_pread(apr_finfo_t *finfo, apr_int32_t wanted,
+                                        apr_dir_t *thedir, apr_pool_t *pool);
+
+    
+/**
  * Rewind the directory to the first entry.
  * @param thedir the directory descriptor to rewind.
  */                        
Index: file_io/unix/dir.c
===================================================================
--- file_io/unix/dir.c  (revision 1862036)
+++ file_io/unix/dir.c  (working copy)
@@ -133,6 +133,12 @@
 apr_status_t apr_dir_read(apr_finfo_t *finfo, apr_int32_t wanted,
                           apr_dir_t *thedir)
 {
+    return apr_dir_pread(finfo, wanted, thedir, thedir->pool);
+}
+
+apr_status_t apr_dir_pread(apr_finfo_t *finfo, apr_int32_t wanted,
+                           apr_dir_t *thedir, apr_pool_t *pool)
+{
     apr_status_t ret = 0;
 #ifdef DIRENT_TYPE
     apr_filetype_e type;
@@ -242,7 +248,7 @@
         apr_cpystrn(end, thedir->entry->d_name, 
                     sizeof fspec - (end - fspec));
 
-        ret = apr_stat(finfo, fspec, APR_FINFO_LINK | wanted, thedir->pool);
+        ret = apr_stat(finfo, fspec, APR_FINFO_LINK | wanted, pool);
         /* We passed a stack name that will disappear */
         finfo->fname = NULL;
     }
@@ -254,7 +260,7 @@
         /* We don't bail because we fail to stat, when we are only -required-
          * to readdir... but the result will be APR_INCOMPLETE
          */
-        finfo->pool = thedir->pool;
+        finfo->pool = pool;
         finfo->valid = 0;
 #ifdef DIRENT_TYPE
         if (type != APR_UNKFILE) {
@@ -270,7 +276,7 @@
 #endif
     }
 
-    finfo->name = apr_pstrdup(thedir->pool, thedir->entry->d_name);
+    finfo->name = apr_pstrdup(pool, thedir->entry->d_name);
     finfo->valid |= APR_FINFO_NAME;
 
     if (wanted)
Index: modules/dav/fs/repos.c
===================================================================
--- modules/dav/fs/repos.c      (revision 1862052)
+++ modules/dav/fs/repos.c      (working copy)
@@ -1490,6 +1490,7 @@
 {
     const dav_walk_params *params = fsctx->params;
     apr_pool_t *pool = params->pool;
+    apr_pool_t *iterpool;
     apr_status_t status;
     dav_error *err = NULL;
     int isdir = fsctx->res1.collection;
@@ -1509,6 +1510,9 @@
         return NULL;
     }
 
+    apr_pool_create(&iterpool, pool);
+    apr_pool_tag(iterpool, "mod_dav_fs-walker");
+    
     /* put a trailing slash onto the directory, in preparation for appending
      * files to it as we discovery them within the directory */
     dav_check_bufsize(pool, &fsctx->path1, DAV_BUFFER_PAD);
@@ -1536,9 +1540,14 @@
         /* ### need a better error */
         return dav_new_error(pool, HTTP_NOT_FOUND, 0, status, NULL);
     }
-    while ((apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp)) == APR_SUCCESS) {
+
+    while (1) {
         apr_size_t len;
 
+        apr_pool_clear(iterpool);
+        status = apr_dir_pread(&dirent, APR_FINFO_DIRENT, dirp, iterpool);
+        if (status)
+            break;
         len = strlen(dirent.name);
 
         /* avoid recursing into our current, parent, or state directories */
@@ -1570,7 +1579,7 @@
         dav_buffer_place_mem(pool, &fsctx->path1, dirent.name, len + 1, 0);
 
         status = apr_stat(&fsctx->info1.finfo, fsctx->path1.buf,
-                          DAV_FINFO_MASK, pool);
+                          DAV_FINFO_MASK, iterpool);
         if (status != APR_SUCCESS && status != APR_INCOMPLETE) {
             /* woah! where'd it go? */
             /* ### should have a better error here */
@@ -1642,7 +1651,8 @@
 
     /* ### check the return value of this? */
     apr_dir_close(dirp);
-
+    apr_pool_destroy(iterpool);
+    
     if (err != NULL)
         return err;
 

Reply via email to