โฌข DragonFlyBSD Kernel Audit
DF-3655 / harness.c
โ† back to finding โ†“ download raw
/*
 * DF-3655 harness โ€” NULL deref through the second WARN_ON in
 * amdgpu_dm_atomic_commit_tail() (sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:4888-4890):
 *
 *   4888  status = dc_stream_get_status(dm_new_crtc_state->stream);
 *   4889  WARN_ON(!status);
 *   4890  WARN_ON(!status->plane_count);   <-- dereferences `status` even
 *                                             when the previous WARN_ON just
 *                                             reported it NULL
 *
 * WARN_ON() evaluates and prints but does NOT stop execution, so line 4890
 * faults.  status==NULL is reachable when the stream is not present in the
 * current DC context (e.g. right after a failed dc_commit_state(), which is
 * only WARN_ON'd at amdgpu_dm.c:4837 and execution continues).
 *
 * Expected: WARN_ON(!status) = 1 printed, then SIGSEGV reading
 * offsetof(dc_stream_status, plane_count) โ€” small offset, unmapped page 0.
 */
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>

struct dc_stream_status {
	int primary_otg_inst;
	int stream_enc_inst;
	int plane_count;	/* verbatim field order, dc/dc_stream.h */
	struct dc_plane_state *plane_states[6];
};

#define WARN_ON(x) (printf("WARN_ON(%s) = %d at %s:%d\n", #x, \
			   (int)(x), __FILE__, __LINE__), fflush(stdout))

int main(void)
{
	struct dc_stream_status *status = NULL; /* dc_stream_get_status() miss */

	printf("offsetof(dc_stream_status, plane_count) ~ 0x%zx\n",
	       offsetof(struct dc_stream_status, plane_count));
	fflush(stdout);

	WARN_ON(!status);		/* amdgpu_dm.c:4889 โ€” prints, continues */
	WARN_ON(!status->plane_count);	/* amdgpu_dm.c:4890 โ€” NULL DEREF */

	printf("NOT REACHED\n");
	return 0;
}