DragonFlyBSD Kernel Audit
DF-1345 / trigger.c
← back to finding ↓ download raw
/* AMD powerplay smumgr div0 triplet (DF-1323/1325/1345)
 * Ports the slope computation from iceland/fiji/polaris10 thermal_setup_fan_table.
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <signal.h>
#include <setjmp.h>

static jmp_buf jb;
static void on_fpe(int s){ longjmp(jb,1); }

/* vulnerable slope computation */
static int slope_vuln(uint16_t usTMin, uint16_t usTMed, uint16_t usTHigh,
                      uint16_t usPWMMin, uint16_t usPWMMed, uint16_t usPWMHigh,
                      uint32_t duty100, uint16_t *s1, uint16_t *s2){
    uint32_t t_diff1 = usTMed - usTMin;            /* uint16 promotes to int; assign to u32 */
    uint32_t t_diff2 = usTHigh - usTMed;
    uint32_t pwm_diff1 = usPWMMed - usPWMMin;
    uint32_t pwm_diff2 = usPWMHigh - usPWMMed;
    *s1 = (uint16_t)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
    *s2 = (uint16_t)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);
    return 0;
}

/* fixed slope computation */
static int slope_fixed(uint16_t usTMin, uint16_t usTMed, uint16_t usTHigh,
                       uint16_t usPWMMin, uint16_t usPWMMed, uint16_t usPWMHigh,
                       uint32_t duty100, uint16_t *s1, uint16_t *s2){
    uint32_t t_diff1 = usTMed - usTMin;
    uint32_t t_diff2 = usTHigh - usTMed;
    if (t_diff1 == 0 || t_diff2 == 0) return 1;     /* disable microcode fan control */
    uint32_t pwm_diff1 = usPWMMed - usPWMMin;
    uint32_t pwm_diff2 = usPWMHigh - usPWMMed;
    *s1 = (uint16_t)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
    *s2 = (uint16_t)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);
    return 0;
}

int main(void){
    signal(SIGFPE, on_fpe);
    /* malicious VBIOS: usTMed == usTMin -> t_diff1 == 0 */
    uint16_t TMin=2500, TMed=2500, THigh=3500;     /* 0.01C units */
    uint16_t PWMMin=100, PWMMed=5000, PWMHigh=10000;
    uint32_t duty100=100;
    uint16_t s1, s2;

    printf("== BEFORE-FIX (vulnerable) ==\n");
    if (setjmp(jb)==0){
        slope_vuln(TMin,TMed,THigh,PWMMin,PWMMed,PWMHigh,duty100,&s1,&s2);
        printf("slope1=%u slope2=%u (no crash)\n", s1, s2);
    } else {
        printf("BUG: SIGFPE — integer divide by zero in slope1/slope2 (VBIOS t_diff==0)\n");
    }
    printf("== AFTER-FIX ==\n");
    int rc = slope_fixed(TMin,TMed,THigh,PWMMin,PWMMed,PWMHigh,duty100,&s1,&s2);
    if (rc == 0) printf("slope1=%u slope2=%u\n", s1, s2);
    else printf("FIX: t_diff==0 detected, fan control disabled (return 0)\n");
    return 0;
}