⬢ DragonFlyBSD Kernel Audit
DF-4748 / static_proof.log
← back to finding ↓ download raw
STEP 1: sole dc_sink->priv assignment site (mst_types.c:243), never cleared:
/home/maxx/dfbsd/dfbsd/sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c:243:		dc_sink->priv = aconnector;
STEP 2: helpers deref stream->sink->priv with value-only guard (197/289/317), guard at 199:
197:	aconnector = stream->sink->priv; 289:	aconnector = stream->sink->priv; 317:	aconnector = stream->sink->priv; 
199:	if (!aconnector || !aconnector->mst_port)
STEP 3: destroy path drops sink ref + connector ref but never clears sink->priv (mst_types.c:386-397):
12:	aconnector->port = NULL;
15:		dc_link_remove_remote_sink(aconnector->dc_link, aconnector->dc_sink);
16:		dc_sink_release(aconnector->dc_sink);
23:	drm_connector_put(connector);
STEP 4: connector destroy is a raw kfree (amdgpu_dm.c:3187):
3187:	kfree(connector);
STEP 5: dc_stream_state holds its own dc_sink reference (dc_stream.c:113), so the sink outlives the connector:
113:	dc_sink_release(stream->sink);
STEP 6: dc_link_remove_remote_sink (dc.c:1823) only edits remote_sinks[]; enabled streams stay live in the context:
	if (!link->sink_count) {
	for (i = 0; i < link->sink_count; i++) {
		if (link->remote_sinks[i] == sink) {
			dc_sink_release(sink);
			link->remote_sinks[i] = NULL;
			while (i < link->sink_count - 1) {
				link->remote_sinks[i] = link->remote_sinks[i+1];
			link->remote_sinks[i] = NULL;
			link->sink_count--;
STEP 7: later commits reach the helpers with the old stream (dc_link.c:2621 enable / 2637 disable):
2621:			allocate_mst_payload(pipe_ctx);
2637:		deallocate_mst_payload(pipe_ctx);
STEP 8: destroy runs asynchronously from the topology worker, unsynchronized with dc commits (drm_dp_mst_topology.c:3116):
3116:		mgr->cbs->destroy_connector(mgr, port->connector);

CHAIN COMPLETE: set-site -> free-without-clear -> ref-outlives -> later deref. QED (static).