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

dma-fence: Add safe access helpers and document the rules

Dma-fence objects currently suffer from a potential use after free problem
where fences exported to userspace and other drivers can outlive the
exporting driver, or the associated data structures.

The discussion on how to address this concluded that adding reference
counting to all the involved objects is not desirable, since it would need
to be very wide reaching and could cause unloadable drivers if another
entity would be holding onto a signaled fence reference potentially
indefinitely.

This patch enables the safe access by introducing and documenting a
contract between fence exporters and users. It documents a set of
contraints and adds helpers which a) drivers with potential to suffer from
the use after free must use and b) users of the dma-fence API must use as
well.

Premise of the design has multiple sides:

1. Drivers (fence exporters) MUST ensure a RCU grace period between
signalling a fence and freeing the driver private data associated with it.

The grace period does not have to follow the signalling immediately but
HAS to happen before data is freed.

2. Users of the dma-fence API marked with such requirement MUST contain
the complete access to the data within a single code block guarded by
rcu_read_lock() and rcu_read_unlock().

The combination of the two ensures that whoever sees the
DMA_FENCE_FLAG_SIGNALED_BIT not set is guaranteed to have access to a
valid fence->lock and valid data potentially accessed by the fence->ops
virtual functions, until the call to rcu_read_unlock().

3. Module unload (fence->ops) disappearing is for now explicitly not
handled. That would required a more complex protection, possibly needing
SRCU instead of RCU to handle callers such as dma_fence_release() and
dma_fence_wait_timeout(), where race between
dma_fence_enable_sw_signaling, signalling, and dereference of
fence->ops->wait() would need a sleeping SRCU context.

Signed-off-by: Tvrtko Ursulin <tvrtko.ursulin@igalia.com>
Reviewed-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
Link: https://lore.kernel.org/r/20250610164226.10817-4-tvrtko.ursulin@igalia.com

authored by

Tvrtko Ursulin and committed by
Tvrtko Ursulin
506aa8b0 4d2f8bc6

+157 -23
+101 -10
drivers/dma-buf/dma-fence.c
··· 511 511 512 512 dma_fence_enable_sw_signaling(fence); 513 513 514 - trace_dma_fence_wait_start(fence); 514 + if (trace_dma_fence_wait_start_enabled()) { 515 + rcu_read_lock(); 516 + trace_dma_fence_wait_start(fence); 517 + rcu_read_unlock(); 518 + } 515 519 if (fence->ops->wait) 516 520 ret = fence->ops->wait(fence, intr, timeout); 517 521 else 518 522 ret = dma_fence_default_wait(fence, intr, timeout); 519 - trace_dma_fence_wait_end(fence); 523 + if (trace_dma_fence_wait_end_enabled()) { 524 + rcu_read_lock(); 525 + trace_dma_fence_wait_end(fence); 526 + rcu_read_unlock(); 527 + } 520 528 return ret; 521 529 } 522 530 EXPORT_SYMBOL(dma_fence_wait_timeout); ··· 541 533 struct dma_fence *fence = 542 534 container_of(kref, struct dma_fence, refcount); 543 535 536 + rcu_read_lock(); 544 537 trace_dma_fence_destroy(fence); 545 538 546 - if (WARN(!list_empty(&fence->cb_list) && 547 - !test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags), 548 - "Fence %s:%s:%llx:%llx released with pending signals!\n", 549 - dma_fence_driver_name(fence), 550 - dma_fence_timeline_name(fence), 551 - fence->context, fence->seqno)) { 539 + if (!list_empty(&fence->cb_list) && 540 + !test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) { 541 + const char __rcu *timeline; 542 + const char __rcu *driver; 552 543 unsigned long flags; 544 + 545 + driver = dma_fence_driver_name(fence); 546 + timeline = dma_fence_timeline_name(fence); 547 + 548 + WARN(1, 549 + "Fence %s:%s:%llx:%llx released with pending signals!\n", 550 + rcu_dereference(driver), rcu_dereference(timeline), 551 + fence->context, fence->seqno); 553 552 554 553 /* 555 554 * Failed to signal before release, likely a refcounting issue. ··· 570 555 dma_fence_signal_locked(fence); 571 556 spin_unlock_irqrestore(fence->lock, flags); 572 557 } 558 + 559 + rcu_read_unlock(); 573 560 574 561 if (fence->ops->release) 575 562 fence->ops->release(fence); ··· 999 982 */ 1000 983 void dma_fence_describe(struct dma_fence *fence, struct seq_file *seq) 1001 984 { 985 + const char __rcu *timeline; 986 + const char __rcu *driver; 987 + 988 + rcu_read_lock(); 989 + 990 + timeline = dma_fence_timeline_name(fence); 991 + driver = dma_fence_driver_name(fence); 992 + 1002 993 seq_printf(seq, "%s %s seq %llu %ssignalled\n", 1003 - dma_fence_driver_name(fence), 1004 - dma_fence_timeline_name(fence), 994 + rcu_dereference(driver), 995 + rcu_dereference(timeline), 1005 996 fence->seqno, 1006 997 dma_fence_is_signaled(fence) ? "" : "un"); 998 + 999 + rcu_read_unlock(); 1007 1000 } 1008 1001 EXPORT_SYMBOL(dma_fence_describe); 1009 1002 ··· 1082 1055 BIT(DMA_FENCE_FLAG_SEQNO64_BIT)); 1083 1056 } 1084 1057 EXPORT_SYMBOL(dma_fence_init64); 1058 + 1059 + /** 1060 + * dma_fence_driver_name - Access the driver name 1061 + * @fence: the fence to query 1062 + * 1063 + * Returns a driver name backing the dma-fence implementation. 1064 + * 1065 + * IMPORTANT CONSIDERATION: 1066 + * Dma-fence contract stipulates that access to driver provided data (data not 1067 + * directly embedded into the object itself), such as the &dma_fence.lock and 1068 + * memory potentially accessed by the &dma_fence.ops functions, is forbidden 1069 + * after the fence has been signalled. Drivers are allowed to free that data, 1070 + * and some do. 1071 + * 1072 + * To allow safe access drivers are mandated to guarantee a RCU grace period 1073 + * between signalling the fence and freeing said data. 1074 + * 1075 + * As such access to the driver name is only valid inside a RCU locked section. 1076 + * The pointer MUST be both queried and USED ONLY WITHIN a SINGLE block guarded 1077 + * by the &rcu_read_lock and &rcu_read_unlock pair. 1078 + */ 1079 + const char __rcu *dma_fence_driver_name(struct dma_fence *fence) 1080 + { 1081 + RCU_LOCKDEP_WARN(!rcu_read_lock_held(), 1082 + "RCU protection is required for safe access to returned string"); 1083 + 1084 + if (!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) 1085 + return fence->ops->get_driver_name(fence); 1086 + else 1087 + return "detached-driver"; 1088 + } 1089 + EXPORT_SYMBOL(dma_fence_driver_name); 1090 + 1091 + /** 1092 + * dma_fence_timeline_name - Access the timeline name 1093 + * @fence: the fence to query 1094 + * 1095 + * Returns a timeline name provided by the dma-fence implementation. 1096 + * 1097 + * IMPORTANT CONSIDERATION: 1098 + * Dma-fence contract stipulates that access to driver provided data (data not 1099 + * directly embedded into the object itself), such as the &dma_fence.lock and 1100 + * memory potentially accessed by the &dma_fence.ops functions, is forbidden 1101 + * after the fence has been signalled. Drivers are allowed to free that data, 1102 + * and some do. 1103 + * 1104 + * To allow safe access drivers are mandated to guarantee a RCU grace period 1105 + * between signalling the fence and freeing said data. 1106 + * 1107 + * As such access to the driver name is only valid inside a RCU locked section. 1108 + * The pointer MUST be both queried and USED ONLY WITHIN a SINGLE block guarded 1109 + * by the &rcu_read_lock and &rcu_read_unlock pair. 1110 + */ 1111 + const char __rcu *dma_fence_timeline_name(struct dma_fence *fence) 1112 + { 1113 + RCU_LOCKDEP_WARN(!rcu_read_lock_held(), 1114 + "RCU protection is required for safe access to returned string"); 1115 + 1116 + if (!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) 1117 + return fence->ops->get_driver_name(fence); 1118 + else 1119 + return "signaled-timeline"; 1120 + } 1121 + EXPORT_SYMBOL(dma_fence_timeline_name);
+22 -9
include/linux/dma-fence.h
··· 378 378 struct dma_fence_cb *cb); 379 379 void dma_fence_enable_sw_signaling(struct dma_fence *fence); 380 380 381 - static inline const char *dma_fence_driver_name(struct dma_fence *fence) 382 - { 383 - return fence->ops->get_driver_name(fence); 384 - } 385 - 386 - static inline const char *dma_fence_timeline_name(struct dma_fence *fence) 387 - { 388 - return fence->ops->get_timeline_name(fence); 389 - } 381 + /** 382 + * DOC: Safe external access to driver provided object members 383 + * 384 + * All data not stored directly in the dma-fence object, such as the 385 + * &dma_fence.lock and memory potentially accessed by functions in the 386 + * &dma_fence.ops table, MUST NOT be accessed after the fence has been signalled 387 + * because after that point drivers are allowed to free it. 388 + * 389 + * All code accessing that data via the dma-fence API (or directly, which is 390 + * discouraged), MUST make sure to contain the complete access within a 391 + * &rcu_read_lock and &rcu_read_unlock pair. 392 + * 393 + * Some dma-fence API handles this automatically, while other, as for example 394 + * &dma_fence_driver_name and &dma_fence_timeline_name, leave that 395 + * responsibility to the caller. 396 + * 397 + * To enable this scheme to work drivers MUST ensure a RCU grace period elapses 398 + * between signalling the fence and freeing the said data. 399 + * 400 + */ 401 + const char __rcu *dma_fence_driver_name(struct dma_fence *fence); 402 + const char __rcu *dma_fence_timeline_name(struct dma_fence *fence); 390 403 391 404 /** 392 405 * dma_fence_is_signaled_locked - Return an indication if the fence
+34 -4
include/trace/events/dma_fence.h
··· 34 34 __entry->seqno) 35 35 ); 36 36 37 - DEFINE_EVENT(dma_fence, dma_fence_emit, 37 + /* 38 + * Safe only for call sites which are guaranteed to not race with fence 39 + * signaling,holding the fence->lock and having checked for not signaled, or the 40 + * signaling path itself. 41 + */ 42 + DECLARE_EVENT_CLASS(dma_fence_unsignaled, 43 + 44 + TP_PROTO(struct dma_fence *fence), 45 + 46 + TP_ARGS(fence), 47 + 48 + TP_STRUCT__entry( 49 + __string(driver, fence->ops->get_driver_name(fence)) 50 + __string(timeline, fence->ops->get_timeline_name(fence)) 51 + __field(unsigned int, context) 52 + __field(unsigned int, seqno) 53 + ), 54 + 55 + TP_fast_assign( 56 + __assign_str(driver); 57 + __assign_str(timeline); 58 + __entry->context = fence->context; 59 + __entry->seqno = fence->seqno; 60 + ), 61 + 62 + TP_printk("driver=%s timeline=%s context=%u seqno=%u", 63 + __get_str(driver), __get_str(timeline), __entry->context, 64 + __entry->seqno) 65 + ); 66 + 67 + DEFINE_EVENT(dma_fence_unsignaled, dma_fence_emit, 38 68 39 69 TP_PROTO(struct dma_fence *fence), 40 70 41 71 TP_ARGS(fence) 42 72 ); 43 73 44 - DEFINE_EVENT(dma_fence, dma_fence_init, 74 + DEFINE_EVENT(dma_fence_unsignaled, dma_fence_init, 45 75 46 76 TP_PROTO(struct dma_fence *fence), 47 77 ··· 85 55 TP_ARGS(fence) 86 56 ); 87 57 88 - DEFINE_EVENT(dma_fence, dma_fence_enable_signal, 58 + DEFINE_EVENT(dma_fence_unsignaled, dma_fence_enable_signal, 89 59 90 60 TP_PROTO(struct dma_fence *fence), 91 61 92 62 TP_ARGS(fence) 93 63 ); 94 64 95 - DEFINE_EVENT(dma_fence, dma_fence_signaled, 65 + DEFINE_EVENT(dma_fence_unsignaled, dma_fence_signaled, 96 66 97 67 TP_PROTO(struct dma_fence *fence), 98 68