#ifndef _TDFX_H #define _TDFX_H #include #include /* membase0 register offsets */ #define STATUS 0x00 #define PCIINIT0 0x04 #define SIPMONITOR 0x08 #define LFBMEMORYCONFIG 0x0c #define MISCINIT0 0x10 #define MISCINIT1 0x14 #define DRAMINIT0 0x18 #define DRAMINIT1 0x1c #define AGPINIT 0x20 #define TMUGBEINIT 0x24 #define VGAINIT0 0x28 #define VGAINIT1 0x2c #define DRAMCOMMAND 0x30 #define DRAMDATA 0x34 /* reserved 0x38 */ /* reserved 0x3c */ #define PLLCTRL0 0x40 #define PLLCTRL1 0x44 #define PLLCTRL2 0x48 #define DACMODE 0x4c #define DACADDR 0x50 #define DACDATA 0x54 #define RGBMAXDELTA 0x58 #define VIDPROCCFG 0x5c #define HWCURPATADDR 0x60 #define HWCURLOC 0x64 #define HWCURC0 0x68 #define HWCURC1 0x6c #define VIDINFORMAT 0x70 #define VIDINSTATUS 0x74 #define VIDSERPARPORT 0x78 #define VIDINXDELTA 0x7c #define VIDININITERR 0x80 #define VIDINYDELTA 0x84 #define VIDPIXBUFTHOLD 0x88 #define VIDCHRMIN 0x8c #define VIDCHRMAX 0x90 #define VIDCURLIN 0x94 #define VIDSCREENSIZE 0x98 #define VIDOVRSTARTCRD 0x9c #define VIDOVRENDCRD 0xa0 #define VIDOVRDUDX 0xa4 #define VIDOVRDUDXOFF 0xa8 #define VIDOVRDVDY 0xac /* ... */ #define VIDOVRDVDYOFF 0xe0 #define VIDDESKSTART 0xe4 #define VIDDESKSTRIDE 0xe8 #define VIDINADDR0 0xec #define VIDINADDR1 0xf0 #define VIDINADDR2 0xf4 #define VIDINSTRIDE 0xf8 #define VIDCUROVRSTART 0xfc #define INTCTRL (0x00100000 + 0x04) #define CLIP0MIN (0x00100000 + 0x08) #define CLIP0MAX (0x00100000 + 0x0c) #define DSTBASE (0x00100000 + 0x10) #define DSTFORMAT (0x00100000 + 0x14) #define SRCBASE (0x00100000 + 0x34) #define COMMANDEXTRA_2D (0x00100000 + 0x38) #define CLIP1MIN (0x00100000 + 0x4c) #define CLIP1MAX (0x00100000 + 0x50) #define SRCFORMAT (0x00100000 + 0x54) #define SRCSIZE (0x00100000 + 0x58) #define SRCXY (0x00100000 + 0x5c) #define COLORBACK (0x00100000 + 0x60) #define COLORFORE (0x00100000 + 0x64) #define DSTSIZE (0x00100000 + 0x68) #define DSTXY (0x00100000 + 0x6c) #define COMMAND_2D (0x00100000 + 0x70) #define LAUNCH_2D (0x00100000 + 0x80) #define COMMAND_3D (0x00200000 + 0x120) /* register bitfields (not all, only as needed) */ /* COMMAND_2D reg. values */ #define TDFX_ROP_COPY 0xcc /* src */ #define TDFX_ROP_INVERT 0x55 /* NOT dst */ #define TDFX_ROP_XOR 0x66 /* src XOR dst */ #define AUTOINC_DSTX BIT(10) #define AUTOINC_DSTY BIT(11) #define COMMAND_2D_FILLRECT 0x05 #define COMMAND_2D_S2S_BITBLT 0x01 /* screen to screen */ #define COMMAND_2D_H2S_BITBLT 0x03 /* host to screen */ #define COMMAND_3D_NOP 0x00 #define STATUS_RETRACE BIT(6) #define STATUS_BUSY BIT(9) #define MISCINIT1_CLUT_INV BIT(0) #define MISCINIT1_2DBLOCK_DIS BIT(15) #define DRAMINIT0_SGRAM_NUM BIT(26) #define DRAMINIT0_SGRAM_TYPE BIT(27) #define DRAMINIT0_SGRAM_TYPE_MASK (BIT(27) | BIT(28) | BIT(29)) #define DRAMINIT0_SGRAM_TYPE_SHIFT 27 #define DRAMINIT1_MEM_SDRAM BIT(30) #define VGAINIT0_VGA_DISABLE BIT(0) #define VGAINIT0_EXT_TIMING BIT(1) #define VGAINIT0_8BIT_DAC BIT(2) #define VGAINIT0_EXT_ENABLE BIT(6) #define VGAINIT0_WAKEUP_3C3 BIT(8) #define VGAINIT0_LEGACY_DISABLE BIT(9) #define VGAINIT0_ALT_READBACK BIT(10) #define VGAINIT0_FAST_BLINK BIT(11) #define VGAINIT0_EXTSHIFTOUT BIT(12) #define VGAINIT0_DECODE_3C6 BIT(13) #define VGAINIT0_SGRAM_HBLANK_DISABLE BIT(22) #define VGAINIT1_MASK 0x1fffff #define VIDCFG_VIDPROC_ENABLE BIT(0) #define VIDCFG_CURS_X11 BIT(1) #define VIDCFG_INTERLACE BIT(3) #define VIDCFG_HALF_MODE BIT(4) #define VIDCFG_DESK_ENABLE BIT(7) #define VIDCFG_CLUT_BYPASS BIT(10) #define VIDCFG_2X BIT(26) #define VIDCFG_HWCURSOR_ENABLE BIT(27) #define VIDCFG_PIXFMT_SHIFT 18 #define DACMODE_2X BIT(0) /* I2C bit locations in the VIDSERPARPORT register */ #define DDC_ENAB 0x00040000 #define DDC_SCL_OUT 0x00080000 #define DDC_SDA_OUT 0x00100000 #define DDC_SCL_IN 0x00200000 #define DDC_SDA_IN 0x00400000 #define I2C_ENAB 0x00800000 #define I2C_SCL_OUT 0x01000000 #define I2C_SDA_OUT 0x02000000 #define I2C_SCL_IN 0x04000000 #define I2C_SDA_IN 0x08000000 /* VGA rubbish, need to change this for multihead support */ #define MISC_W 0x3c2 #define MISC_R 0x3cc #define SEQ_I 0x3c4 #define SEQ_D 0x3c5 #define CRT_I 0x3d4 #define CRT_D 0x3d5 #define ATT_IW 0x3c0 #define IS1_R 0x3da #define GRA_I 0x3ce #define GRA_D 0x3cf #ifdef __KERNEL__ struct banshee_reg { /* VGA rubbish */ unsigned char att[21]; unsigned char crt[25]; unsigned char gra[9]; unsigned char misc[1]; unsigned char seq[5]; /* Banshee extensions */ unsigned char ext[2]; unsigned long vidcfg; unsigned long vidpll; unsigned long mempll; unsigned long gfxpll; unsigned long dacmode; unsigned long vgainit0; unsigned long vgainit1; unsigned long screensize; unsigned long stride; unsigned long cursloc; unsigned long curspataddr; unsigned long cursc0; unsigned long cursc1; unsigned long startaddr; unsigned long clip0min; unsigned long clip0max; unsigned long clip1min; unsigned long clip1max; unsigned long miscinit0; }; struct tdfx_par; struct tdfxfb_i2c_chan { struct tdfx_par *par; struct i2c_adapter adapter; struct i2c_algo_bit_data algo; }; struct tdfx_par { u32 max_pixclock; u32 palette[16]; void __iomem *regbase_virt; unsigned long iobase; int wc_cookie; #ifdef CONFIG_FB_3DFX_I2C struct tdfxfb_i2c_chan chan[2]; #endif }; #endif /* __KERNEL__ */ #endif /* _TDFX_H */ ux/net-next.git/tree/?h=nds-private-remove&id=191e885a2e130e639bb0c8ee350d7047294f2ce6'>9bf9f9b4971df55a46e0a6750d3f6cd37bf1d9f0 /include/trace parent49def1853334396f948dcb4cedb9347abb318df5 (diff)
firmware: fix NULL pointer dereference in __fw_load_abort()
Since commit 5d47ec02c37ea6 ("firmware: Correct handling of fw_state_wait() return value") fw_load_abort() could be called twice and lead us to a kernel crash. This happens only when the firmware fallback mechanism (regular or custom) is used. The fallback mechanism exposes a sysfs interface for userspace to upload a file and notify the kernel when the file is loaded and ready, or to cancel an upload by echo'ing -1 into on the loading file: echo -n "-1" > /sys/$DEVPATH/loading This will call fw_load_abort(). Some distributions actually have a udev rule in place to *always* immediately cancel all firmware fallback mechanism requests (Debian), they have: $ cat /lib/udev/rules.d/50-firmware.rules # stub for immediately telling the kernel that userspace firmware loading # failed; necessary to avoid long timeouts with CONFIG_FW_LOADER_USER_HELPER=y SUBSYSTEM=="firmware", ACTION=="add", ATTR{loading}="-1 Distributions with this udev rule would run into this crash only if the fallback mechanism is used. Since most distributions disable by default using the fallback mechanism (CONFIG_FW_LOADER_USER_HELPER_FALLBACK), this would typicaly mean only 2 drivers which *require* the fallback mechanism could typically incur a crash: drivers/firmware/dell_rbu.c and the drivers/leds/leds-lp55xx-common.c driver. Distributions enabling CONFIG_FW_LOADER_USER_HELPER_FALLBACK by default are obviously more exposed to this crash. The crash happens because after commit 5b029624948d ("firmware: do not use fw_lock for fw_state protection") and subsequent fix commit 5d47ec02c37ea6 ("firmware: Correct handling of fw_state_wait() return value") a race can happen between this cancelation and the firmware fw_state_wait_timeout() being woken up after a state change with which fw_load_abort() as that calls swake_up(). Upon error fw_state_wait_timeout() will also again call fw_load_abort() and trigger a null reference. At first glance we could just fix this with a !buf check on fw_load_abort() before accessing buf->fw_st, however there is a logical issue in having a state machine used for the fallback mechanism and preventing access from it once we abort as its inside the buf (buf->fw_st). The firmware_class.c code is setting the buf to NULL to annotate an abort has occurred. Replace this mechanism by simply using the state check instead. All the other code in place already uses similar checks for aborting as well so no further changes are needed. An oops can be reproduced with the new fw_fallback.sh fallback mechanism cancellation test. Either cancelling the fallback mechanism or the custom fallback mechanism triggers a crash. mcgrof@piggy ~/linux-next/tools/testing/selftests/firmware (git::20170111-fw-fixes)$ sudo ./fw_fallback.sh ./fw_fallback.sh: timeout works ./fw_fallback.sh: firmware comparison works ./fw_fallback.sh: fallback mechanism works [ this then sits here when it is trying the cancellation test ] Kernel log: test_firmware: loading 'nope-test-firmware.bin' misc test_firmware: Direct firmware load for nope-test-firmware.bin failed with error -2 misc test_firmware: Falling back to user helper BUG: unable to handle kernel NULL pointer dereference at 0000000000000038 IP: _request_firmware+0xa27/0xad0 PGD 0 Oops: 0000 [#1] SMP Modules linked in: test_firmware(E) ... etc ... CPU: 1 PID: 1396 Comm: fw_fallback.sh Tainted: G W E 4.10.0-rc3-next-20170111+ #30 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.10.1-0-g8891697-prebuilt.qemu-project.org 04/01/2014 task: ffff9740b27f4340 task.stack: ffffbb15c0bc8000 RIP: 0010:_request_firmware+0xa27/0xad0 RSP: 0018:ffffbb15c0bcbd10 EFLAGS: 00010246 RAX: 00000000fffffffe RBX: ffff9740afe5aa80 RCX: 0000000000000000 RDX: ffff9740b27f4340 RSI: 0000000000000283 RDI: 0000000000000000 RBP: ffffbb15c0bcbd90 R08: ffffbb15c0bcbcd8 R09: 0000000000000000 R10: 0000000894a0d4b1 R11: 000000000000008c R12: ffffffffc0312480 R13: 0000000000000005 R14: ffff9740b1c32400 R15: 00000000000003e8 FS: 00007f8604422700(0000) GS:ffff9740bfc80000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000038 CR3: 000000012164c000 CR4: 00000000000006e0 Call Trace: request_firmware+0x37/0x50 trigger_request_store+0x79/0xd0 [test_firmware] dev_attr_store+0x18/0x30 sysfs_kf_write+0x37/0x40 kernfs_fop_write+0x110/0x1a0 __vfs_write+0x37/0x160 ? _cond_resched+0x1a/0x50 vfs_write+0xb5/0x1a0 SyS_write+0x55/0xc0 ? trace_do_page_fault+0x37/0xd0 entry_SYSCALL_64_fastpath+0x1e/0xad RIP: 0033:0x7f8603f49620 RSP: 002b:00007fff6287b788 EFLAGS: 00000246 ORIG_RAX: 0000000000000001 RAX: ffffffffffffffda RBX: 000055c307b110a0 RCX: 00007f8603f49620 RDX: 0000000000000016 RSI: 000055c3084d8a90 RDI: 0000000000000001 RBP: 0000000000000016 R08: 000000000000c0ff R09: 000055c3084d6336 R10: 000055c307b108b0 R11: 0000000000000246 R12: 000055c307b13c80 R13: 000055c3084d6320 R14: 0000000000000000 R15: 00007fff6287b950 Code: 9f 64 84 e8 9c 61 fe ff b8 f4 ff ff ff e9 6b f9 ff ff 48 c7 c7 40 6b 8d 84 89 45 a8 e8 43 84 18 00 49 8b be 00 03 00 00 8b 45 a8 <83> 7f 38 02 74 08 e8 6e ec ff ff 8b 45 a8 49 c7 86 00 03 00 00 RIP: _request_firmware+0xa27/0xad0 RSP: ffffbb15c0bcbd10 CR2: 0000000000000038 ---[ end trace 6d94ac339c133e6f ]--- Fixes: 5d47ec02c37e ("firmware: Correct handling of fw_state_wait() return value") Reported-and-Tested-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reported-and-Tested-by: Patrick Bruenn <p.bruenn@beckhoff.com> Reported-by: Chris Wilson <chris@chris-wilson.co.uk> CC: <stable@vger.kernel.org> [3.10+] Signed-off-by: Luis R. Rodriguez <mcgrof@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'include/trace')