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

dma-debug: fix locking bug in check_unmap()

In check_unmap() it is possible to get into a dead-locked state if
dma_mapping_error is called. The problem is that the bucket is locked in
check_unmap, and locked again by debug_dma_mapping_error which is called
by dma_mapping_error. To resolve that we must release the lock on the
bucket before making the call to dma_mapping_error.

[akpm@linux-foundation.org: restore 80-col trickery to be consistent with the rest of the file]
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Cc: Joerg Roedel <joro@8bytes.org>
Reviewed-by: Shuah Khan <shuah.khan@hp.com>
Tested-by: Shuah Khan <shuah.khan@hp.com>
Cc: Jakub Kicinski <kubakici@wp.pl>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Alexander Duyck and committed by
Linus Torvalds
8d640a51 0ef1594c

+12 -9
+12 -9
lib/dma-debug.c
··· 862 862 entry = bucket_find_exact(bucket, ref); 863 863 864 864 if (!entry) { 865 + /* must drop lock before calling dma_mapping_error */ 866 + put_hash_bucket(bucket, &flags); 867 + 865 868 if (dma_mapping_error(ref->dev, ref->dev_addr)) { 866 869 err_printk(ref->dev, NULL, 867 - "DMA-API: device driver tries " 868 - "to free an invalid DMA memory address\n"); 869 - return; 870 + "DMA-API: device driver tries to free an " 871 + "invalid DMA memory address\n"); 872 + } else { 873 + err_printk(ref->dev, NULL, 874 + "DMA-API: device driver tries to free DMA " 875 + "memory it has not allocated [device " 876 + "address=0x%016llx] [size=%llu bytes]\n", 877 + ref->dev_addr, ref->size); 870 878 } 871 - err_printk(ref->dev, NULL, "DMA-API: device driver tries " 872 - "to free DMA memory it has not allocated " 873 - "[device address=0x%016llx] [size=%llu bytes]\n", 874 - ref->dev_addr, ref->size); 875 - goto out; 879 + return; 876 880 } 877 881 878 882 if (ref->size != entry->size) { ··· 940 936 hash_bucket_del(entry); 941 937 dma_entry_free(entry); 942 938 943 - out: 944 939 put_hash_bucket(bucket, &flags); 945 940 } 946 941