⬢ DragonFlyBSD Kernel Audit
DF-3889 / boot_ps_null.c
← back to finding ↓ download raw
/*
 * DF-3889 harness — boot_ps NULL dereference in kv_dpm_disable ->
 * kv_update_current_ps -> kv_get_ps(NULL) -> rps->ps_priv.
 *
 * Mirrors sys/dev/drm/amd/amdgpu/kv_dpm.c:371-376 (kv_get_ps),
 * :1223-1233 (kv_update_current_ps) and the sink at :1400
 * (kv_update_current_ps(adev, adev->pm.dpm.boot_ps)), for a VBIOS power
 * table with no ATOM_PPLIB_CLASSIFICATION_BOOT state
 * (kv_parse_pplib_non_clock_info :2672-2675 never sets boot_ps; sw_init
 * :3026 assigns the NULL into current_ps/requested_ps).
 *
 * Modes:
 *   ./t          accounting: prints the dereference site / offset.
 *   ./t crash    executes the verbatim NULL deref -> SIGSEGV at
 *                (void *)0 + offsetof(struct amdgpu_ps, ps_priv)
 *                (kernel equivalent: page fault in kernel mode -> panic).
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <sys/mman.h>

/* 1:1 struct amdgpu_ps (amdgpu_dpm.h:55-69) */
struct amdgpu_ps {
	uint32_t caps;		/* vbios flags */
	uint32_t class;		/* vbios flags */
	uint32_t class2;	/* vbios flags */
	uint32_t vclk;
	uint32_t dclk;
	uint32_t evclk;
	uint32_t ecclk;
	uint8_t  vce_active;
	uint8_t  _pad[3];
	int32_t  vce_level;	/* enum amd_vce_level */
	void    *ps_priv;	/* asic priv */
};

struct kv_ps { int dummy; };

/* verbatim kv_dpm.c:371-376 */
static struct kv_ps *kv_get_ps(struct amdgpu_ps *rps)
{
	struct kv_ps *ps = rps->ps_priv;	/* <-- NULL deref when rps == NULL */
	return ps;
}

/* verbatim kv_dpm.c:1223-1233 shape (first line only is the fault) */
static void kv_update_current_ps(struct amdgpu_ps *rps)
{
	struct kv_ps *new_ps = kv_get_ps(rps);
	(void)new_ps;
}

int main(int argc, char **argv)
{
	int crash = (argc > 1 && strcmp(argv[1], "crash") == 0);

	/* VBIOS power table has states, none classified BOOT:
	 *   kv_parse_pplib_non_clock_info (:2672) never assigns boot_ps
	 *   kv_dpm_sw_init (:3026): current_ps = requested_ps = boot_ps = NULL
	 *   dpm inits and runs fine ...
	 *   suspend / driver detach -> kv_dpm_hw_fini -> kv_dpm_disable (:1400):
	 *       kv_update_current_ps(adev, adev->pm.dpm.boot_ps);  // NULL
	 */
	struct amdgpu_ps *boot_ps = NULL;

	printf("offsetof(struct amdgpu_ps, ps_priv) = %zu\n",
	       offsetof(struct amdgpu_ps, ps_priv));
	printf("kv_dpm_disable (:1400) calls kv_update_current_ps(adev, boot_ps=NULL)\n");
	printf("  -> kv_get_ps(NULL) (:371-373): rps->ps_priv reads *(void**)(NULL + %zu)\n",
	       offsetof(struct amdgpu_ps, ps_priv));
	fflush(stdout);

	if (crash) {
		printf("crash mode: executing verbatim deref\n");
		fflush(stdout);
		kv_update_current_ps(boot_ps);
		printf("NOT REACHED\n");
	}
	return 0;
}