当前位置: 首页 > 服务与支持 > 产品升级公告 > 安全漏洞公告

服务与支持Support

漏洞名称:Apache Subversion 'libsvn_fs_fs/tree.c'拒绝服务漏洞

     发表日期:2015-08-31 10:19:17

漏洞名称:Apache Subversion 'libsvn_fs_fs/tree.c'拒绝服务漏洞(CVE-2015-0202)
BUGTRAQ ID:76446
CVE ID:CVE-2015-0202
受影响系统:
Apache Group Subversion 1.8.0-1.8.11
详细信息:
 
Subversion是一款开源多用户版本控制系统,支持非ASCII文本和二进制数据。
 
 
 
Subversion HTTPD服务器1.8.0-1.8.11版本,FSFS库在处理某些REPORT请求时,由于没有及时解除已经分配的缓存节点,可能会耗尽服务器上可用的内存,在实现上存在拒绝服务漏洞,成功利用后可造成大量的内存资源耗尽,导致拒绝服务。
 
 
漏洞来源:
Evgeny Kotkov
解决方案:
厂商补丁:
 
Apache Group
------------
Apache Group已经为此发布了一个安全公告(CVE-2015-0202-advisory)以及相应补丁:
 
CVE-2015-0202-advisory:Subversion HTTP servers with FSFS repositories are vulnerable to a   remotely triggerable excessive memory use with certain REPORT requests.
 
链接:https://subversion.apache.org/security/CVE-2015-0202-advisory.txt
 
 
 
补丁下载:
 
 
 
========
 
 
 
1.8.11的补丁:
 
[[[
 
Index: subversion/libsvn_fs_fs/tree.c
 
===================================================================
 
--- subversion/libsvn_fs_fs/tree.c    (revision 1655679)
 
+++ subversion/libsvn_fs_fs/tree.c    (working copy)
 
@@ -127,7 +127,6 @@ typedef struct fs_txn_root_data_t
 
 static svn_error_t * get_dag(dag_node_t **dag_node_p,
 
                              svn_fs_root_t *root,
 
                              const char *path,
 
-                             svn_boolean_t needs_lock_cache,
 
                              apr_pool_t *pool);
 
 
 
 static svn_fs_root_t *make_revision_root(svn_fs_t *fs, svn_revnum_t rev,
 
@@ -178,34 +177,10 @@ typedef struct cache_entry_t
 
  */
 
 enum { BUCKET_COUNT = 256 };
 
 
 
-/* Each pool that has received a DAG node, will hold at least on lock on
 
-   our cache to ensure that the node remains valid despite being allocated
 
-   in the cache's pool.  This is the structure to represent the lock.
 
- */
 
-typedef struct cache_lock_t
 
-{
 
-  /* pool holding the lock */
 
-  apr_pool_t *pool;
 
-
 
-  /* cache being locked */
 
-  fs_fs_dag_cache_t *cache;
 
-
 
-  /* next lock. NULL at EOL */
 
-  struct cache_lock_t *next;
 
-
 
-  /* previous lock. NULL at list head. Only then this==cache->first_lock */
 
-  struct cache_lock_t *prev;
 
-} cache_lock_t;
 
-
 
 /* The actual cache structure.  All nodes will be allocated in POOL.
 
    When the number of INSERTIONS (i.e. objects created form that pool)
 
    exceeds a certain threshold, the pool will be cleared and the cache
 
    with it.
 
-
 
-   To ensure that nodes returned from this structure remain valid, the
 
-   cache will get locked for the lifetime of the _receiving_ pools (i.e.
 
-   those in which we would allocate the node if there was no cache.).
 
-   The cache will only be cleared FIRST_LOCK is 0.
 
  */
 
 struct fs_fs_dag_cache_t
 
 {
 
@@ -221,47 +196,8 @@ struct fs_fs_dag_cache_t
 
   /* Property lookups etc. have a very high locality (75% re-hit).
 
      Thus, remember the last hit location for optimistic lookup. */
 
   apr_size_t last_hit;
 
-
 
-  /* List of receiving pools that are still alive. */
 
-  cache_lock_t *first_lock;
 
 };
 
 
 
-/* Cleanup function to be called when a receiving pool gets cleared.
 
-   Unlocks the cache once.
 
- */
 
-static apr_status_t
 
-unlock_cache(void *baton_void)
 
-{
 
-  cache_lock_t *lock = baton_void;
 
-
 
-  /* remove lock from chain. Update the head */
 
-  if (lock->next)
 
-    lock->next->prev = lock->prev;
 
-  if (lock->prev)
 
-    lock->prev->next = lock->next;
 
-  else
 
-    lock->cache->first_lock = lock->next;
 
-
 
-  return APR_SUCCESS;
 
-}
 
-
 
-/* Cleanup function to be called when the cache itself gets destroyed.
 
-   In that case, we must unregister all unlock requests.
 
- */
 
-static apr_status_t
 
-unregister_locks(void *baton_void)
 
-{
 
-  fs_fs_dag_cache_t *cache = baton_void;
 
-  cache_lock_t *lock;
 
-
 
-  for (lock = cache->first_lock; lock; lock = lock->next)
 
-    apr_pool_cleanup_kill(lock->pool,
 
-                          lock,
 
-                          unlock_cache);
 
-
 
-  return APR_SUCCESS;
 
-}
 
-
 
 fs_fs_dag_cache_t*
 
 svn_fs_fs__create_dag_cache(apr_pool_t *pool)
 
 {
 
@@ -268,59 +204,15 @@ svn_fs_fs__create_dag_cache(apr_pool_t *pool)
 
   fs_fs_dag_cache_t *result = apr_pcalloc(pool, sizeof(*result));
 
   result->pool = svn_pool_create(pool);
 
 
 
-  apr_pool_cleanup_register(pool,
 
-                            result,
 
-                            unregister_locks,
 
-                            apr_pool_cleanup_null);
 
-
 
   return result;
 
 }
 
 
 
-/* Prevent the entries in CACHE from being destroyed, for as long as the
 
-   POOL lives.
 
- */
 
-static void
 
-lock_cache(fs_fs_dag_cache_t* cache, apr_pool_t *pool)
 
-{
 
-  /* we only need to lock / unlock once per pool.  Since we will often ask
 
-     for multiple nodes with the same pool, we can reduce the overhead.
 
-     However, if e.g. pools are being used in an alternating pattern,
 
-     we may lock the cache more than once for the same pool (and register
 
-     just as many cleanup actions).
 
-   */
 
-  cache_lock_t *lock = cache->first_lock;
 
-
 
-  /* try to find an existing lock for POOL.
 
-     But limit the time spent on chasing pointers.  */
 
-  int limiter = 8;
 
-  while (lock && --limiter)
 
-      if (lock->pool == pool)
 
-        return;
 
-
 
-  /* create a new lock and put it at the beginning of the lock chain */
 
-  lock = apr_palloc(pool, sizeof(*lock));
 
-  lock->cache = cache;
 
-  lock->pool = pool;
 
-  lock->next = cache->first_lock;
 
-  lock->prev = NULL;
 
-
 
-  if (cache->first_lock)
 
-    cache->first_lock->prev = lock;
 
-  cache->first_lock = lock;
 
-
 
-  /* instruct POOL to remove the look upon cleanup */
 
-  apr_pool_cleanup_register(pool,
 
-                            lock,
 
-                            unlock_cache,
 
-                            apr_pool_cleanup_null);
 
-}
 
-
 
 /* Clears the CACHE at regular intervals (destroying all cached nodes)
 
  */
 
 static void
 
 auto_clear_dag_cache(fs_fs_dag_cache_t* cache)
 
 {
 
-  if (cache->first_lock == NULL && cache->insertions > BUCKET_COUNT)
 
+  if (cache->insertions > BUCKET_COUNT)
 
     {
 
       svn_pool_clear(cache->pool);
 
 
 
@@ -433,18 +325,12 @@ locate_cache(svn_cache__t **cache,
 
     }
 
 }
 
 
 
-/* Return NODE for PATH from ROOT's node cache, or NULL if the node
 
-   isn't cached; read it from the FS. *NODE remains valid until either
 
-   POOL or the FS gets cleared or destroyed (whichever comes first).
 
-
 
-   Since locking can be expensive and POOL may be long-living, for
 
-   nodes that will not need to survive the next call to this function,
 
-   set NEEDS_LOCK_CACHE to FALSE. */
 
+/* Return NODE_P for PATH from ROOT's node cache, or NULL if the node
 
+   isn't cached; read it from the FS. *NODE_P is allocated in POOL. */
 
 static svn_error_t *
 
 dag_node_cache_get(dag_node_t **node_p,
 
                    svn_fs_root_t *root,
 
                    const char *path,
 
-                   svn_boolean_t needs_lock_cache,
 
                    apr_pool_t *pool)
 
 {
 
   svn_boolean_t found;
 
@@ -466,25 +352,23 @@ dag_node_cache_get(dag_node_t **node_p,
 
       if (bucket->node == NULL)
 
         {
 
           locate_cache(&cache, &key, root, path, pool);
 
-          SVN_ERR(svn_cache__get((void **)&node, &found, cache, key,
 
-                                 ffd->dag_node_cache->pool));
 
+          SVN_ERR(svn_cache__get((void **)&node, &found, cache, key, pool));
 
           if (found && node)
 
             {
 
               /* Patch up the FS, since this might have come from an old FS
 
               * object. */
 
               svn_fs_fs__dag_set_fs(node, root->fs);
 
-              bucket->node = node;
 
+
 
+              /* Retain the DAG node in L1 cache. */
 
+              bucket->node = svn_fs_fs__dag_dup(node,
 
+                                                ffd->dag_node_cache->pool);
 
             }
 
         }
 
       else
 
         {
 
-          node = bucket->node;
 
+          /* Copy the node from L1 cache into the passed-in POOL. */
 
+          node = svn_fs_fs__dag_dup(bucket->node, pool);
 
         }
 
-
 
-      /* if we found a node, make sure it remains valid at least as long
 
-         as it would when allocated in POOL. */
 
-      if (node && needs_lock_cache)
 
-        lock_cache(ffd->dag_node_cache, pool);
 
     }
 
   else
 
     {
 
@@ -822,7 +706,7 @@ get_copy_inheritance(copy_id_inherit_t *inherit_p,
 
   SVN_ERR(svn_fs_fs__dag_get_copyroot(&copyroot_rev, &copyroot_path,
 
                                       child->node));
 
   SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, fs, copyroot_rev, pool));
 
-  SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path, FALSE, pool));
 
+  SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path, pool));
 
   copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
 
 
 
   if (svn_fs_fs__id_compare(copyroot_id, child_id) == -1)
 
@@ -938,7 +822,7 @@ open_path(parent_path_t **parent_path_p,
 
     {
 
       directory = svn_dirent_dirname(path, pool);
 
       if (directory[1] != 0) /* root nodes are covered anyway */
 
-        SVN_ERR(dag_node_cache_get(&here, root, directory, TRUE, pool));
 
+        SVN_ERR(dag_node_cache_get(&here, root, directory, pool));
 
     }
 
 
 
   /* did the shortcut work? */
 
@@ -998,8 +882,8 @@ open_path(parent_path_t **parent_path_p,
 
              element if we already know the lookup to fail for the
 
              complete path. */
 
           if (next || !(flags & open_path_uncached))
 
-            SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far,
 
-                                       TRUE, pool));
 
+            SVN_ERR(dag_node_cache_get(&cached_node, root, path_so_far, pool));
 
+
 
           if (cached_node)
 
             child = cached_node;
 
           else
 
@@ -1136,8 +1020,7 @@ make_path_mutable(svn_fs_root_t *root,
 
                                           parent_path->node));
 
       SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, root->fs,
 
                                        copyroot_rev, pool));
 
-      SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path,
 
-                      FALSE, pool));
 
+      SVN_ERR(get_dag(&copyroot_node, copyroot_root, copyroot_path, pool));
 
 
 
       child_id = svn_fs_fs__dag_get_id(parent_path->node);
 
       copyroot_id = svn_fs_fs__dag_get_id(copyroot_node);
 
@@ -1174,16 +1057,11 @@ make_path_mutable(svn_fs_root_t *root,
 
 
 
 /* Open the node identified by PATH in ROOT.  Set DAG_NODE_P to the
 
    node we find, allocated in POOL.  Return the error
 
-   SVN_ERR_FS_NOT_FOUND if this node doesn't exist.
 
-
 
-   Since locking can be expensive and POOL may be long-living, for
 
-   nodes that will not need to survive the next call to this function,
 
-   set NEEDS_LOCK_CACHE to FALSE. */
 
+   SVN_ERR_FS_NOT_FOUND if this node doesn't exist. */
 
 static svn_error_t *
 
 get_dag(dag_node_t **dag_node_p,
 
         svn_fs_root_t *root,
 
         const char *path,
 
-        svn_boolean_t needs_lock_cache,
 
         apr_pool_t *pool)
 
 {
 
   parent_path_t *parent_path;
 
@@ -1192,7 +1070,7 @@ get_dag(dag_node_t **dag_node_p,
 
   /* First we look for the DAG in our cache
 
      (if the path may be canonical). */
 
   if (*path == '/')
 
-    SVN_ERR(dag_node_cache_get(&node, root, path, needs_lock_cache, pool));
 
+    SVN_ERR(dag_node_cache_get(&node, root, path, pool));
 
 
 
   if (! node)
 
     {
 
@@ -1202,8 +1080,7 @@ get_dag(dag_node_t **dag_node_p,
 
           path = svn_fs__canonicalize_abspath(path, pool);
 
 
 
           /* Try again with the corrected path. */
 
-          SVN_ERR(dag_node_cache_get(&node, root, path, needs_lock_cache,
 
-                                     pool));
 
+          SVN_ERR(dag_node_cache_get(&node, root, path, pool));
 
         }
 
 
 
       if (! node)
 
@@ -1281,7 +1158,7 @@ svn_fs_fs__node_id(const svn_fs_id_t **id_p,
 
     {
 
       dag_node_t *node;
 
 
 
-      SVN_ERR(get_dag(&node, root, path, FALSE, pool));
 
+      SVN_ERR(get_dag(&node, root, path, pool));
 
       *id_p = svn_fs_fs__id_copy(svn_fs_fs__dag_get_id(node), pool);
 
     }
 
   return SVN_NO_ERROR;
 
@@ -1296,7 +1173,7 @@ svn_fs_fs__node_created_rev(svn_revnum_t *revision
 
 {
 
   dag_node_t *node;
 
 
 
-  SVN_ERR(get_dag(&node, root, path, FALSE, pool));
 
+  SVN_ERR(get_dag(&node, root, path, pool));
 
   return svn_fs_fs__dag_get_revision(revision, node, pool);
 
 }
 
 
 
@@ -1311,7 +1188,7 @@ fs_node_created_path(const char **created_path,
 
 {
 
   dag_node_t *node;
 
 
 
-  SVN_ERR(get_dag(&node, root, path, TRUE, pool));
 
+  SVN_ERR(get_dag(&node, root, path, pool));
 
   *created_path = svn_fs_fs__dag_get_created_path(node);
 
 
 
   return SVN_NO_ERROR;
 
@@ -1375,7 +1252,7 @@ fs_node_prop(svn_string_t **value_p,
 
   dag_node_t *node;
 
   apr_hash_t *proplist;
 
 
 
-  SVN_ERR(get_dag(&node, root, path, FALSE, pool));
 
+  SVN_ERR(get_dag(&node, root, path, pool));
 
   SVN_ERR(svn_fs_fs__dag_get_proplist(&proplist, node, pool));
 
   *value_p = NULL;
 
   if (proplist)
 
@@ -1398,7 +1275,7 @@ fs_node_proplist(apr_hash_t **table_p,
 
   apr_hash_t *table;
 
   dag_node_t *node;
 
 
 
-  SVN_ERR(get_dag(&node, root, path, FALSE, pool));
 
+  SVN_ERR(get_dag(&node, root, path, pool));
 
   SVN_ERR(svn_fs_fs__dag_get_proplist(&table, node, pool));
 
   *table_p = table ? table : apr_hash_make(pool);
 
 
 
@@ -1515,8 +1392,8 @@ fs_props_changed(svn_boolean_t *changed_p,
 
       (SVN_ERR_FS_GENERAL, NULL,
 
        _("Cannot compare property value between two different filesystems"));
 
 
 
-  SVN_ERR(get_dag(&node1, root1, path1, TRUE, pool));
 
-  SVN_ERR(get_dag(&node2, root2, path2, TRUE, pool));
 
+  SVN_ERR(get_dag(&node1, root1, path1, pool));
 
+  SVN_ERR(get_dag(&node2, root2, path2, pool));
 
   return svn_fs_fs__dag_things_different(changed_p, NULL,
 
                                          node1, node2);
 
 }
 
@@ -1529,7 +1406,7 @@ fs_props_changed(svn_boolean_t *changed_p,
 
 static svn_error_t *
 
 get_root(dag_node_t **node, svn_fs_root_t *root, apr_pool_t *pool)
 
 {
 
-  return get_dag(node, root, "/", TRUE, pool);
 
+  return get_dag(node, root, "/", pool);
 
 }
 
 
 
 
 
@@ -2193,7 +2070,7 @@ fs_dir_entries(apr_hash_t **table_p,
 
   dag_node_t *node;
 
 
 
   /* Get the entries for this path in the caller's pool. */
 
-  SVN_ERR(get_dag(&node, root, path, FALSE, pool));
 
+  SVN_ERR(get_dag(&node, root, path, pool));
 
   return svn_fs_fs__dag_dir_entries(table_p, node, pool);
 
 }
 
 
 
@@ -2365,7 +2242,7 @@ copy_helper(svn_fs_root_t *from_root,
 
        _("Copy from mutable tree not currently supported"));
 
 
 
   /* Get the NODE for FROM_PATH in FROM_ROOT.*/
 
-  SVN_ERR(get_dag(&from_node, from_root, from_path, TRUE, pool));
 
+  SVN_ERR(get_dag(&from_node, from_root, from_path, pool));
 
 
 
   /* Build up the parent path from TO_PATH in TO_ROOT.  If the last
 
      component does not exist, it's not that big a deal.  We'll just
 
@@ -2442,7 +2319,7 @@ copy_helper(svn_fs_root_t *from_root,
 
                                             pool));
 
 
 
       /* Make a record of this modification in the changes table. */
 
-      SVN_ERR(get_dag(&new_node, to_root, to_path, TRUE, pool));
 
+      SVN_ERR(get_dag(&new_node, to_root, to_path, pool));
 
       SVN_ERR(add_change(to_root->fs, txn_id, to_path,
 
                          svn_fs_fs__dag_get_id(new_node), kind, FALSE, FALSE,
 
                          svn_fs_fs__dag_node_kind(from_node),
 
@@ -2553,7 +2430,7 @@ fs_copied_from(svn_revnum_t *rev_p,
 
     {
 
       /* There is no cached entry, look it up the old-fashioned
 
          way. */
 
-      SVN_ERR(get_dag(&node, root, path, TRUE, pool));
 
+      SVN_ERR(get_dag(&node, root, path, pool));
 
       SVN_ERR(svn_fs_fs__dag_get_copyfrom_rev(&copyfrom_rev, node));
 
       SVN_ERR(svn_fs_fs__dag_get_copyfrom_path(&copyfrom_path, node));
 
     }
 
@@ -2628,7 +2505,7 @@ fs_file_length(svn_filesize_t *length_p,
 
   dag_node_t *file;
 
 
 
   /* First create a dag_node_t from the root/path pair. */
 
-  SVN_ERR(get_dag(&file, root, path, FALSE, pool));
 
+  SVN_ERR(get_dag(&file, root, path, pool));
 
 
 
   /* Now fetch its length */
 
   return svn_fs_fs__dag_file_length(length_p, file, pool);
 
@@ -2647,7 +2524,7 @@ fs_file_checksum(svn_checksum_t **checksum,
 
 {
 
   dag_node_t *file;
 
 
 
-  SVN_ERR(get_dag(&file, root, path, FALSE, pool));
 
+  SVN_ERR(get_dag(&file, root, path, pool));
 
   return svn_fs_fs__dag_file_checksum(checksum, file, kind, pool);
 
 }
 
 
 
@@ -2666,7 +2543,7 @@ fs_file_contents(svn_stream_t **contents,
 
   svn_stream_t *file_stream;
 
 
 
   /* First create a dag_node_t from the root/path pair. */
 
-  SVN_ERR(get_dag(&node, root, path, FALSE, pool));
 
+  SVN_ERR(get_dag(&node, root, path, pool));
 
 
 
   /* Then create a readable stream from the dag_node_t. */
 
   SVN_ERR(svn_fs_fs__dag_get_contents(&file_stream, node, pool));
 
@@ -2689,7 +2566,7 @@ fs_try_process_file_contents(svn_boolean_t *succes
 
                              apr_pool_t *pool)
 
 {
 
   dag_node_t *node;
 
-  SVN_ERR(get_dag(&node, root, path, FALSE, pool));
 
+  SVN_ERR(get_dag(&node, root, path, pool));
 
 
 
   return svn_fs_fs__dag_try_process_file_contents(success, node,
 
                                                   processor, baton, pool);
 
@@ -3071,8 +2948,8 @@ fs_contents_changed(svn_boolean_t *changed_p,
 
         (SVN_ERR_FS_GENERAL, NULL, _("'%s' is not a file"), path2);
 
   }
 
 
 
-  SVN_ERR(get_dag(&node1, root1, path1, TRUE, pool));
 
-  SVN_ERR(get_dag(&node2, root2, path2, TRUE, pool));
 
+  SVN_ERR(get_dag(&node1, root1, path1, pool));
 
+  SVN_ERR(get_dag(&node2, root2, path2, pool));
 
   return svn_fs_fs__dag_things_different(NULL, changed_p,
 
                                          node1, node2);
 
 }
 
@@ -3092,10 +2969,10 @@ fs_get_file_delta_stream(svn_txdelta_stream_t **st
 
   dag_node_t *source_node, *target_node;
 
 
 
   if (source_root && source_path)
 
-    SVN_ERR(get_dag(&source_node, source_root, source_path, TRUE, pool));
 
+    SVN_ERR(get_dag(&source_node, source_root, source_path, pool));
 
   else
 
     source_node = NULL;
 
-  SVN_ERR(get_dag(&target_node, target_root, target_path, TRUE, pool));
 
+  SVN_ERR(get_dag(&target_node, target_root, target_path, pool));
 
 
 
   /* Create a delta stream that turns the source into the target.  */
 
   return svn_fs_fs__dag_get_file_delta_stream(stream_p, source_node,
 
@@ -3588,7 +3465,7 @@ history_prev(void *baton, apr_pool_t *pool)
 
 
 
       SVN_ERR(svn_fs_fs__revision_root(&copyroot_root, fs, copyroot_rev,
 
                                        pool));
 
-      SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, FALSE, pool));
 
+      SVN_ERR(get_dag(&node, copyroot_root, copyroot_path, pool));
 
       copy_dst = svn_fs_fs__dag_get_created_path(node);
 
 
 
       /* If our current path was the very destination of the copy,
 
@@ -3785,7 +3662,7 @@ crawl_directory_dag_for_mergeinfo(svn_fs_root_t *r
 
       svn_pool_clear(iterpool);
 
 
 
       kid_path = svn_fspath__join(this_path, dirent->name, iterpool);
 
-      SVN_ERR(get_dag(&kid_dag, root, kid_path, TRUE, iterpool));
 
+      SVN_ERR(get_dag(&kid_dag, root, kid_path, iterpool));
 
 
 
       SVN_ERR(svn_fs_fs__dag_has_mergeinfo(&has_mergeinfo, kid_dag));
 
       SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down, kid_dag));
 
@@ -4031,7 +3908,7 @@ add_descendant_mergeinfo(svn_mergeinfo_catalog_t r
 
   dag_node_t *this_dag;
 
   svn_boolean_t go_down;
 
 
 
-  SVN_ERR(get_dag(&this_dag, root, path, TRUE, scratch_pool));
 
+  SVN_ERR(get_dag(&this_dag, root, path, scratch_pool));
 
   SVN_ERR(svn_fs_fs__dag_has_descendants_with_mergeinfo(&go_down,
 
                                                         this_dag));
 
   if (go_down)
 
]]]