mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	Btrfs: fix memory leaks on walking backrefs failure
When walking backrefs, we may iterate every inode's extent and add/merge them into ulist, and the caller will free memory from ulist. However, if we fail to allocate inode's extents element memory or ulist_add() fail to allocate memory, we won't add allocated memory into ulist, and the caller won't free some allocated memory thus memory leaks happen. Signed-off-by: Wang Shilong <wangsl.fnst@cn.fujitsu.com> Signed-off-by: Josef Bacik <jbacik@fb.com> Signed-off-by: Chris Mason <clm@fb.com>
This commit is contained in:
		
							parent
							
								
									bf54f412f0
								
							
						
					
					
						commit
						f05c474688
					
				
					 1 changed files with 18 additions and 7 deletions
				
			
		| 
						 | 
					@ -66,6 +66,16 @@ static int check_extent_in_eb(struct btrfs_key *key, struct extent_buffer *eb,
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void free_inode_elem_list(struct extent_inode_elem *eie)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct extent_inode_elem *eie_next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (; eie; eie = eie_next) {
 | 
				
			||||||
 | 
							eie_next = eie->next;
 | 
				
			||||||
 | 
							kfree(eie);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int find_extent_in_eb(struct extent_buffer *eb, u64 wanted_disk_byte,
 | 
					static int find_extent_in_eb(struct extent_buffer *eb, u64 wanted_disk_byte,
 | 
				
			||||||
				u64 extent_item_pos,
 | 
									u64 extent_item_pos,
 | 
				
			||||||
				struct extent_inode_elem **eie)
 | 
									struct extent_inode_elem **eie)
 | 
				
			||||||
| 
						 | 
					@ -275,6 +285,7 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
 | 
				
			||||||
					old = old->next;
 | 
										old = old->next;
 | 
				
			||||||
				old->next = eie;
 | 
									old->next = eie;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								eie = NULL;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
next:
 | 
					next:
 | 
				
			||||||
		ret = btrfs_next_old_item(root, path, time_seq);
 | 
							ret = btrfs_next_old_item(root, path, time_seq);
 | 
				
			||||||
| 
						 | 
					@ -282,6 +293,8 @@ static int add_all_parents(struct btrfs_root *root, struct btrfs_path *path,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ret > 0)
 | 
						if (ret > 0)
 | 
				
			||||||
		ret = 0;
 | 
							ret = 0;
 | 
				
			||||||
 | 
						else if (ret < 0)
 | 
				
			||||||
 | 
							free_inode_elem_list(eie);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -845,6 +858,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
 | 
				
			||||||
	struct list_head prefs_delayed;
 | 
						struct list_head prefs_delayed;
 | 
				
			||||||
	struct list_head prefs;
 | 
						struct list_head prefs;
 | 
				
			||||||
	struct __prelim_ref *ref;
 | 
						struct __prelim_ref *ref;
 | 
				
			||||||
 | 
						struct extent_inode_elem *eie = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	INIT_LIST_HEAD(&prefs);
 | 
						INIT_LIST_HEAD(&prefs);
 | 
				
			||||||
	INIT_LIST_HEAD(&prefs_delayed);
 | 
						INIT_LIST_HEAD(&prefs_delayed);
 | 
				
			||||||
| 
						 | 
					@ -958,7 +972,6 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
 | 
				
			||||||
				goto out;
 | 
									goto out;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (ref->count && ref->parent) {
 | 
							if (ref->count && ref->parent) {
 | 
				
			||||||
			struct extent_inode_elem *eie = NULL;
 | 
					 | 
				
			||||||
			if (extent_item_pos && !ref->inode_list) {
 | 
								if (extent_item_pos && !ref->inode_list) {
 | 
				
			||||||
				u32 bsz;
 | 
									u32 bsz;
 | 
				
			||||||
				struct extent_buffer *eb;
 | 
									struct extent_buffer *eb;
 | 
				
			||||||
| 
						 | 
					@ -993,6 +1006,7 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
 | 
				
			||||||
					eie = eie->next;
 | 
										eie = eie->next;
 | 
				
			||||||
				eie->next = ref->inode_list;
 | 
									eie->next = ref->inode_list;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								eie = NULL;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		list_del(&ref->list);
 | 
							list_del(&ref->list);
 | 
				
			||||||
		kmem_cache_free(btrfs_prelim_ref_cache, ref);
 | 
							kmem_cache_free(btrfs_prelim_ref_cache, ref);
 | 
				
			||||||
| 
						 | 
					@ -1011,7 +1025,8 @@ static int find_parent_nodes(struct btrfs_trans_handle *trans,
 | 
				
			||||||
		list_del(&ref->list);
 | 
							list_del(&ref->list);
 | 
				
			||||||
		kmem_cache_free(btrfs_prelim_ref_cache, ref);
 | 
							kmem_cache_free(btrfs_prelim_ref_cache, ref);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if (ret < 0)
 | 
				
			||||||
 | 
							free_inode_elem_list(eie);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1019,7 +1034,6 @@ static void free_leaf_list(struct ulist *blocks)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct ulist_node *node = NULL;
 | 
						struct ulist_node *node = NULL;
 | 
				
			||||||
	struct extent_inode_elem *eie;
 | 
						struct extent_inode_elem *eie;
 | 
				
			||||||
	struct extent_inode_elem *eie_next;
 | 
					 | 
				
			||||||
	struct ulist_iterator uiter;
 | 
						struct ulist_iterator uiter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ULIST_ITER_INIT(&uiter);
 | 
						ULIST_ITER_INIT(&uiter);
 | 
				
			||||||
| 
						 | 
					@ -1027,10 +1041,7 @@ static void free_leaf_list(struct ulist *blocks)
 | 
				
			||||||
		if (!node->aux)
 | 
							if (!node->aux)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		eie = (struct extent_inode_elem *)(uintptr_t)node->aux;
 | 
							eie = (struct extent_inode_elem *)(uintptr_t)node->aux;
 | 
				
			||||||
		for (; eie; eie = eie_next) {
 | 
							free_inode_elem_list(eie);
 | 
				
			||||||
			eie_next = eie->next;
 | 
					 | 
				
			||||||
			kfree(eie);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		node->aux = 0;
 | 
							node->aux = 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue