DragonFlyBSD Kernel Audit
DF-1334 / trigger.c
← back to finding ↓ download raw
/* DF-1334 rv770_dpm multiple div0 sinks */
#include <stdio.h>
#include <stdint.h>
#include <signal.h>
#include <setjmp.h>
static jmp_buf jb;
static void on_fpe(int s){ longjmp(jb,1); }

static int populate_smc_t_vuln(int low_sclk, int med_sclk, int high_sclk,
                              int rlp, int lmp, int rmp, int lhp){
    /* a_d = low.sclk*(100-rlp) + med.sclk*lmp ; if both sclk=0 -> div0 */
    int a_n = med_sclk*lmp + low_sclk*(150-rlp); /* R600_AH_DFLT=150 */
    int a_d = low_sclk*(100-rlp) + med_sclk*lmp;
    return (low_sclk*100)/a_d + (a_n/a_d);     /* div0 if a_d==0 */
}
static int populate_smc_t_fixed(int low_sclk, int med_sclk, int high_sclk,
                              int rlp, int lmp, int rmp, int lhp){
    int a_n = med_sclk*lmp + low_sclk*(150-rlp);
    int a_d = low_sclk*(100-rlp) + med_sclk*lmp;
    if (a_d == 0) return 0;       /* guard */
    return (low_sclk*100)/a_d + (a_n/a_d);
}
static int sclk_ss_vuln(int ref_clock, int ref_div, int rate, int pct, int fbdiv){
    int clk_s = ref_clock*5/(ref_div*rate);    /* div0 if rate==0 or ref_div==0 */
    int clk_v = pct*fbdiv/(clk_s*10000);       /* div0 if clk_s==0 */
    return clk_s+clk_v;
}
static int sclk_ss_fixed(int ref_clock, int ref_div, int rate, int pct, int fbdiv){
    if (ref_div==0||rate==0) return 0;
    int clk_s = ref_clock*5/(ref_div*rate);
    if (clk_s==0) return 0;
    int clk_v = pct*fbdiv/(clk_s*10000);
    return clk_s+clk_v;
}
static int vddc_steps_vuln(int vddc_min, int vddc_max, int step){
    return (vddc_max - vddc_min)/step + 1;     /* div0 if step==0 */
}
static int vddc_steps_fixed(int vddc_min, int vddc_max, int step){
    if (step==0) return -1;       /* EINVAL */
    return (vddc_max - vddc_min)/step + 1;
}

int main(void){
    signal(SIGFPE, on_fpe);
    printf("== BEFORE-FIX (vulnerable) ==\n");
    if (setjmp(jb)==0){ populate_smc_t_vuln(0,0,0,5,5,5,5); }
    else printf("BUG: SIGFPE in rv770_populate_smc_t (a_d==0 when VBIOS sclk=0)\n");
    if (setjmp(jb)==0){ sclk_ss_vuln(27000, 1, 0, 10, 50); }
    else printf("BUG: SIGFPE in rv770_populate_sclk_value (VBIOS SS rate<100 -> /100 trunc=0)\n");
    if (setjmp(jb)==0){ vddc_steps_vuln(500, 1100, 0); }
    else printf("BUG: SIGFPE in rv770_construct_vddc_table (step==0 from radeon_atom_get_voltage_step -EINVAL)\n");
    printf("== AFTER-FIX ==\n");
    populate_smc_t_fixed(0,0,0,5,5,5,5); printf("FIX: a_d==0 guarded, no division\n");
    sclk_ss_fixed(27000,1,0,10,50); printf("FIX: rate/ref_div==0 guarded\n");
    vddc_steps_fixed(500,1100,0); printf("FIX: step==0 returns -EINVAL\n");
    return 0;
}