Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

jbd2: fix potential double free

When failing from creating cache jbd2_inode_cache, we will destroy the
previously created cache jbd2_handle_cache twice. This patch fixes
this by moving each cache initialization/destruction to its own
separate, individual function.

Signed-off-by: Chengguang Xu <cgxu519@gmail.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Cc: stable@kernel.org

authored by

Chengguang Xu and committed by
Theodore Ts'o
0d52154b 592acbf1

+63 -38
+33 -20
fs/jbd2/journal.c
··· 2375 2375 static atomic_t nr_journal_heads = ATOMIC_INIT(0); 2376 2376 #endif 2377 2377 2378 - static int jbd2_journal_init_journal_head_cache(void) 2378 + static int __init jbd2_journal_init_journal_head_cache(void) 2379 2379 { 2380 - int retval; 2381 - 2382 - J_ASSERT(jbd2_journal_head_cache == NULL); 2380 + J_ASSERT(!jbd2_journal_head_cache); 2383 2381 jbd2_journal_head_cache = kmem_cache_create("jbd2_journal_head", 2384 2382 sizeof(struct journal_head), 2385 2383 0, /* offset */ 2386 2384 SLAB_TEMPORARY | SLAB_TYPESAFE_BY_RCU, 2387 2385 NULL); /* ctor */ 2388 - retval = 0; 2389 2386 if (!jbd2_journal_head_cache) { 2390 - retval = -ENOMEM; 2391 2387 printk(KERN_EMERG "JBD2: no memory for journal_head cache\n"); 2388 + return -ENOMEM; 2392 2389 } 2393 - return retval; 2390 + return 0; 2394 2391 } 2395 2392 2396 2393 static void jbd2_journal_destroy_journal_head_cache(void) ··· 2633 2636 2634 2637 struct kmem_cache *jbd2_handle_cache, *jbd2_inode_cache; 2635 2638 2636 - static int __init jbd2_journal_init_handle_cache(void) 2639 + static int __init jbd2_journal_init_inode_cache(void) 2637 2640 { 2638 - jbd2_handle_cache = KMEM_CACHE(jbd2_journal_handle, SLAB_TEMPORARY); 2639 - if (jbd2_handle_cache == NULL) { 2640 - printk(KERN_EMERG "JBD2: failed to create handle cache\n"); 2641 - return -ENOMEM; 2642 - } 2641 + J_ASSERT(!jbd2_inode_cache); 2643 2642 jbd2_inode_cache = KMEM_CACHE(jbd2_inode, 0); 2644 - if (jbd2_inode_cache == NULL) { 2645 - printk(KERN_EMERG "JBD2: failed to create inode cache\n"); 2646 - kmem_cache_destroy(jbd2_handle_cache); 2643 + if (!jbd2_inode_cache) { 2644 + pr_emerg("JBD2: failed to create inode cache\n"); 2647 2645 return -ENOMEM; 2648 2646 } 2649 2647 return 0; 2648 + } 2649 + 2650 + static int __init jbd2_journal_init_handle_cache(void) 2651 + { 2652 + J_ASSERT(!jbd2_handle_cache); 2653 + jbd2_handle_cache = KMEM_CACHE(jbd2_journal_handle, SLAB_TEMPORARY); 2654 + if (!jbd2_handle_cache) { 2655 + printk(KERN_EMERG "JBD2: failed to create handle cache\n"); 2656 + return -ENOMEM; 2657 + } 2658 + return 0; 2659 + } 2660 + 2661 + static void jbd2_journal_destroy_inode_cache(void) 2662 + { 2663 + kmem_cache_destroy(jbd2_inode_cache); 2664 + jbd2_inode_cache = NULL; 2650 2665 } 2651 2666 2652 2667 static void jbd2_journal_destroy_handle_cache(void) 2653 2668 { 2654 2669 kmem_cache_destroy(jbd2_handle_cache); 2655 2670 jbd2_handle_cache = NULL; 2656 - kmem_cache_destroy(jbd2_inode_cache); 2657 - jbd2_inode_cache = NULL; 2658 2671 } 2659 2672 2660 2673 /* ··· 2675 2668 { 2676 2669 int ret; 2677 2670 2678 - ret = jbd2_journal_init_revoke_caches(); 2671 + ret = jbd2_journal_init_revoke_record_cache(); 2672 + if (ret == 0) 2673 + ret = jbd2_journal_init_revoke_table_cache(); 2679 2674 if (ret == 0) 2680 2675 ret = jbd2_journal_init_journal_head_cache(); 2681 2676 if (ret == 0) 2682 2677 ret = jbd2_journal_init_handle_cache(); 2678 + if (ret == 0) 2679 + ret = jbd2_journal_init_inode_cache(); 2683 2680 if (ret == 0) 2684 2681 ret = jbd2_journal_init_transaction_cache(); 2685 2682 return ret; ··· 2691 2680 2692 2681 static void jbd2_journal_destroy_caches(void) 2693 2682 { 2694 - jbd2_journal_destroy_revoke_caches(); 2683 + jbd2_journal_destroy_revoke_record_cache(); 2684 + jbd2_journal_destroy_revoke_table_cache(); 2695 2685 jbd2_journal_destroy_journal_head_cache(); 2696 2686 jbd2_journal_destroy_handle_cache(); 2687 + jbd2_journal_destroy_inode_cache(); 2697 2688 jbd2_journal_destroy_transaction_cache(); 2698 2689 jbd2_journal_destroy_slabs(); 2699 2690 }
+20 -12
fs/jbd2/revoke.c
··· 178 178 return NULL; 179 179 } 180 180 181 - void jbd2_journal_destroy_revoke_caches(void) 181 + void jbd2_journal_destroy_revoke_record_cache(void) 182 182 { 183 183 kmem_cache_destroy(jbd2_revoke_record_cache); 184 184 jbd2_revoke_record_cache = NULL; 185 + } 186 + 187 + void jbd2_journal_destroy_revoke_table_cache(void) 188 + { 185 189 kmem_cache_destroy(jbd2_revoke_table_cache); 186 190 jbd2_revoke_table_cache = NULL; 187 191 } 188 192 189 - int __init jbd2_journal_init_revoke_caches(void) 193 + int __init jbd2_journal_init_revoke_record_cache(void) 190 194 { 191 195 J_ASSERT(!jbd2_revoke_record_cache); 192 - J_ASSERT(!jbd2_revoke_table_cache); 193 - 194 196 jbd2_revoke_record_cache = KMEM_CACHE(jbd2_revoke_record_s, 195 197 SLAB_HWCACHE_ALIGN|SLAB_TEMPORARY); 196 - if (!jbd2_revoke_record_cache) 197 - goto record_cache_failure; 198 198 199 + if (!jbd2_revoke_record_cache) { 200 + pr_emerg("JBD2: failed to create revoke_record cache\n"); 201 + return -ENOMEM; 202 + } 203 + return 0; 204 + } 205 + 206 + int __init jbd2_journal_init_revoke_table_cache(void) 207 + { 208 + J_ASSERT(!jbd2_revoke_table_cache); 199 209 jbd2_revoke_table_cache = KMEM_CACHE(jbd2_revoke_table_s, 200 210 SLAB_TEMPORARY); 201 - if (!jbd2_revoke_table_cache) 202 - goto table_cache_failure; 203 - return 0; 204 - table_cache_failure: 205 - jbd2_journal_destroy_revoke_caches(); 206 - record_cache_failure: 211 + if (!jbd2_revoke_table_cache) { 212 + pr_emerg("JBD2: failed to create revoke_table cache\n"); 207 213 return -ENOMEM; 214 + } 215 + return 0; 208 216 } 209 217 210 218 static struct jbd2_revoke_table_s *jbd2_journal_init_revoke_table(int hash_size)
+5 -3
fs/jbd2/transaction.c
··· 42 42 0, 43 43 SLAB_HWCACHE_ALIGN|SLAB_TEMPORARY, 44 44 NULL); 45 - if (transaction_cache) 46 - return 0; 47 - return -ENOMEM; 45 + if (!transaction_cache) { 46 + pr_emerg("JBD2: failed to create transaction cache\n"); 47 + return -ENOMEM; 48 + } 49 + return 0; 48 50 } 49 51 50 52 void jbd2_journal_destroy_transaction_cache(void)
+5 -3
include/linux/jbd2.h
··· 1318 1318 1319 1319 /* Transaction cache support */ 1320 1320 extern void jbd2_journal_destroy_transaction_cache(void); 1321 - extern int jbd2_journal_init_transaction_cache(void); 1321 + extern int __init jbd2_journal_init_transaction_cache(void); 1322 1322 extern void jbd2_journal_free_transaction(transaction_t *); 1323 1323 1324 1324 /* ··· 1446 1446 /* Primary revoke support */ 1447 1447 #define JOURNAL_REVOKE_DEFAULT_HASH 256 1448 1448 extern int jbd2_journal_init_revoke(journal_t *, int); 1449 - extern void jbd2_journal_destroy_revoke_caches(void); 1450 - extern int jbd2_journal_init_revoke_caches(void); 1449 + extern void jbd2_journal_destroy_revoke_record_cache(void); 1450 + extern void jbd2_journal_destroy_revoke_table_cache(void); 1451 + extern int __init jbd2_journal_init_revoke_record_cache(void); 1452 + extern int __init jbd2_journal_init_revoke_table_cache(void); 1451 1453 1452 1454 extern void jbd2_journal_destroy_revoke(journal_t *); 1453 1455 extern int jbd2_journal_revoke (handle_t *, unsigned long long, struct buffer_head *);