Todo: Audit the use of << and >> (shift) operators in gPXE

Per a discussion by mcb30:

[3:11pm] mcb30: rwcr: I am puzzled by this (1<<32) issue
[3:11pm] mcb30: (I just replied to your e-mail)
[3:12pm] ogreinside joined the chat room.
[3:13pm] rwcr: Hmm. I didn't think about the overflow; that *should* make it work.
[3:13pm] mcb30: It doesn't
[3:13pm] rwcr: Aha.
[3:13pm] mcb30: I just tested compiling the damn code
[3:13pm] mcb30: I understand why the x86 code fails, but
[3:13pm] rwcr: The x86 processor truncates CL to the bottom 5 bits when using it as the shift.
[3:13pm] rwcr: So a shift by 32 turns into a shift by 0.
[3:13pm] mcb30: Yes, but that is the kind of artefact that should not be visible at this level
[3:14pm] mcb30: i.e. if x86 has that behaviour, but C says that << is a "pure" shift, then the compiler should insert a >=32 check on every shift
[3:14pm] mdc: 32 mod 32 :)
[3:14pm] mcb30: This must be mandated by the language spec somewhere
[3:15pm] rwcr: I suspect it says shifts by more than the size of the variable have undefined or implementation-defined behavior.
[3:15pm] mcb30: What concerns me is that I suspect I have relied on what I thought would be the correct behaviour in various bits of code over the years
[3:16pm] mdc: https://www.securecoding.cert.org/confluence/display/seccode/INT13-C.+Use+bitwise+operators+only+on+unsigned+operands
[3:16pm] rwcr: "The results of some bitwise operations on signed integers" are implementation-defined.
[3:16pm] mdc: (not quite the case here, but interesting)
[3:16pm] rwcr: Try it with 1u << 32.
[3:16pm] rwcr: C90 6.3, C95 6.5.
[3:17pm] mcb30: I was using 1UL already in my test
[3:17pm] rwcr: Eek.
[3:18pm] rwcr: I get 0 for all types of 1 << (var == 32) shift.
[3:18pm] mcb30: http://paste.etherboot.org:80/?page=view&id=1249931905
[3:19pm] mcb30: How are you forcing var to be variable (i.e. preventing the compiler optimising it to a constant shift)?
[3:19pm] mcb30: (I made it a global variable to prevent this from happening)
[3:19pm] rwcr: Ah, that'd be it - I used a local.
[3:19pm] rwcr: Sure enough, turns out 1 now.
[3:19pm] rwcr: This is rather scary.
[3:20pm] mcb30: I found a mailing list post with the same problem, and someone saying that it is indeed undefined behaviour
[3:21pm] rwcr: Disassembly shows a straight shl %cl,%eax without any checking.
[3:21pm] rwcr: I think the behavior of that opcode changed on the 286 or so to truncate to 5 bits.
[3:23pm] mcb30: 6.5.7 of C99 does indeed say "undefined"
[3:23pm] mcb30: Hmmm
[3:24pm] mcb30: I really don't feel like auditing every use of shift operators in the gPXE codebase
[3:26pm] rwcr: There are 93 shifts by a variable count in gpxe.lkrn.
[3:26pm] rwcr: Sorry, that's left shifts. 19 right shifts.
[3:26pm] ogreinside1 joined the chat room.
[3:26pm] MMlosh left the chat room. ("rebooting router to ensure proper configuration of readonly and readwrite paths")
[3:26pm] mcb30: mdc_mobile: want something to do? ;)
[3:27pm] • mdc suspects another "warnings purge" type of project :)
[3:27pm] mdc: mcb30: I think we need an http://etherboot.org/wiki/todo page :)
[3:27pm] mdc: With karma points for completing tasks therein :)
[3:28pm] mcb30: Social solutions to technical problems :)
[3:28pm] mdc: mcb30: Yes, since social beings need to do the work, it does often make sense :)
[3:28pm] mdc: Ask the Chinese :)
[3:29pm] mdc: Anyway, I take it we need to audit the use of << and >> in gPXE?
[3:29pm] mcb30: Basically, yes
[3:29pm] mcb30: Constant shifts are ok; the compiler already warns about those if they go out-of-range
[3:30pm] mcb30: (which I thought was just it being helpful in the same vein as the "unsigned integer >= 0 is always true" warnings)
[3:30pm] mdc: Can you make a page http://etherboot.org/wiki/todo/audit-the-shifts  and describe the task so I can come back to it?  (and perhaps make a link on http://etherboot.org/wiki/todo ?
[3:31pm] mcb30: etherboot.org/wiki/todo doesn't exist
[3:31pm] mcb30: (yet)
[3:31pm] mdc: :)
[3:31pm] • mdc likes dropping hints :)
[3:31pm] mdc: hee hee
[3:31pm] mcb30: That seems like a "project leader" type task to me
[3:31pm] mdc: alright
[3:32pm] mcb30: :)
[3:32pm] mdc: lol
[3:33pm] mcb30: I did my wiki-contributing for today already
[3:33pm] mcb30: (documenting the SRP boot firmware table)
[3:33pm] mdc: I'm making the ToDo page :)

Here is a list of all the variable shifts in gPXE, extracted with

$ make veryclean
$ make EXTRA_CFLAGS=-fno-inline bin/gpxe.lkrn
$ objdump -d bin/gpxe.lkrn.tmp | grep "s[ah][lr].*%cl," | cut -d: -f1 | xargs addr2line -fe bin/gpxe.lkrn.tmp | \
      perl -p0777e 's|\n/| @ /|g; s|'$PWD'/||g;' | uniq | sort --field-separator=@ --key=2b
outb @ arch/i386/include/gpxe/x86_io.h:129
base64_encode @ core/base64.c:59
bitmap_test @ core/bitmap.c:79
bitmap_set @ core/bitmap.c:95
isspace @ core/ctype.c:37
fetch_uint_setting @ core/settings.c:707
i2c_select @ drivers/bitbash/i2c_bit.c:208
i2c_select @ drivers/bitbash/i2c_bit.c:216
spi_bit_transfer @ drivers/bitbash/spi_bit.c:107
arbel_start_firmware @ drivers/infiniband/arbel.c:1713
arbel_get_limits @ drivers/infiniband/arbel.c:1794
arbel_get_limits @ drivers/infiniband/arbel.c:1798
arbel_get_limits @ drivers/infiniband/arbel.c:1801
arbel_get_limits @ drivers/infiniband/arbel.c:1805
arbel_get_limits @ drivers/infiniband/arbel.c:1809
arbel_get_limits @ drivers/infiniband/arbel.c:1812
arbel_get_limits @ drivers/infiniband/arbel.c:1815
icm_usage @ drivers/infiniband/arbel.c:1834
arbel_alloc_icm @ drivers/infiniband/arbel.c:1946
hermon_bitmask_free @ drivers/infiniband/hermon.c:109
hermon_get_cap @ drivers/infiniband/hermon.c:2156
hermon_get_cap @ drivers/infiniband/hermon.c:2161
hermon_get_cap @ drivers/infiniband/hermon.c:2164
hermon_get_cap @ drivers/infiniband/hermon.c:2169
hermon_get_cap @ drivers/infiniband/hermon.c:2172
icm_usage @ drivers/infiniband/hermon.c:2200
hermon_free_icm @ drivers/infiniband/hermon.c:2465
hermon_create_qp @ drivers/infiniband/hermon.c:976
linda_send_buf_in_use @ drivers/infiniband/linda.c:411
linda_create_recv_wq @ drivers/infiniband/linda.c:624
linda_create_recv_wq @ drivers/infiniband/linda.c:625
linda_destroy_recv_wq @ drivers/infiniband/linda.c:654
linda_destroy_recv_wq @ drivers/infiniband/linda.c:655
corkscrew_probe1 @ drivers/net/3c515.c:727
ath5k_copy_channels @ drivers/net/ath5k/ath5k.c:571
ath5k_hw_start_tx_dma @ drivers/net/ath5k/ath5k_dma.c:146
ath5k_hw_stop_tx_dma @ drivers/net/ath5k/ath5k_dma.c:189
ath5k_hw_set_txdp @ drivers/net/ath5k/ath5k_dma.c:309
ath5k_eeprom_read_pcal_info_2413 @ drivers/net/ath5k/ath5k_eeprom.c:1215
ath5k_eeprom_convert_pcal_info_5111 @ drivers/net/ath5k/ath5k_eeprom.c:680
ath5k_eeprom_read_pcal_info_5112 @ drivers/net/ath5k/ath5k_eeprom.c:958
ath5k_hw_bitswap @ drivers/net/ath5k/ath5k.h:1269
ath5k_hw_rfb_op @ drivers/net/ath5k/ath5k_phy.c:108
ath5k_hw_rfb_op @ drivers/net/ath5k/ath5k_phy.c:113
ath5k_hw_rfb_op @ drivers/net/ath5k/ath5k_phy.c:114
ath5k_hw_rfb_op @ drivers/net/ath5k/ath5k_phy.c:116
ath5k_hw_reset_tx_queue @ drivers/net/ath5k/ath5k_qcu.c:187
ath5k_hw_reset_tx_queue @ drivers/net/ath5k/ath5k_qcu.c:189
ath5k_hw_write_ofdm_timings @ drivers/net/ath5k/ath5k_reset.c:129
read_eeprom @ drivers/net/davicom.c:398
read_eeprom @ drivers/net/davicom.c:407
read_srom_word @ drivers/net/dmfe.c:677
e1000_get_hw_control @ drivers/net/e1000/e1000.c:68
e1000_shift_out_mdi_bits @ drivers/net/e1000/e1000_hw.c:3323
e1000_phy_reset @ drivers/net/e1000/e1000_hw.c:3918
e1000_init_eeprom_params @ drivers/net/e1000/e1000_hw.c:4637
e1000_shift_out_ee_bits @ drivers/net/e1000/e1000_hw.c:4700
e1000_read_mac_addr @ drivers/net/e1000/e1000_hw.c:5729
e1000_mta_set @ drivers/net/e1000/e1000_hw.c:5883
e1000_rar_set @ drivers/net/e1000/e1000_hw.c:5941
e1000_clear_vfta @ drivers/net/e1000/e1000_hw.c:6013
e1000_id_led_init @ drivers/net/e1000/e1000_hw.c:6066
e1000_id_led_init @ drivers/net/e1000/e1000_hw.c:6072
e1000_id_led_init @ drivers/net/e1000/e1000_hw.c:6078
e1000_id_led_init @ drivers/net/e1000/e1000_hw.c:6084
e1000_id_led_init @ drivers/net/e1000/e1000_hw.c:6089
e1000_id_led_init @ drivers/net/e1000/e1000_hw.c:6095
e1000_blink_led_start @ drivers/net/e1000/e1000_hw.c:6189
e1000_blink_led_start @ drivers/net/e1000/e1000_hw.c:6191
e1000_reset_hw @ drivers/net/e1000/e1000_hw.c:625
do_eeprom_cmd @ drivers/net/eepro100.c:353
eepro100_poll @ drivers/net/eepro100.c:548
eepro100_probe @ drivers/net/eepro100.c:646
read_eeprom @ drivers/net/eepro.c:507
mdio_clause45_wait_reset_mmds @ drivers/net/etherfabric.c:286
phantom_poll_link_state @ drivers/net/phantom/phantom.c:1048
phantom_check_boot_enable @ drivers/net/phantom/phantom.c:1944
rtl8169_write_gmii_reg_bit @ drivers/net/r8169.c:348
rtl_poll @ drivers/net/rtl8139.c:426
rtl8225_read @ drivers/net/rtl818x/rtl8185_rtl8225.c:118
rtl8225_read @ drivers/net/rtl818x/rtl8185_rtl8225.c:178
rtl8225_write @ drivers/net/rtl818x/rtl8185_rtl8225.c:74
sis900_read_eeprom @ drivers/net/sis900.c:478
sis900_mdio_read @ drivers/net/sis900.c:546
sky2_mhz @ drivers/net/sky2.c:1888
mdio_read @ drivers/net/sundance.c:813
mdio_write @ drivers/net/sundance.c:847
TLan_MiiSendData @ drivers/net/tlan.c:1189
mdio_read @ drivers/net/tulip.c:625
mdio_write @ drivers/net/tulip.c:684
read_eeprom @ drivers/net/tulip.c:712
read_eeprom @ drivers/net/tulip.c:723
WriteMII @ drivers/net/via-rhine.c:854
w89c840_probe @ drivers/net/w89c840.c:676
eeprom_read @ drivers/net/w89c840.c:753
mdio_read @ drivers/net/w89c840.c:817
nvs_write @ drivers/nvs/nvs.c:126
nvs_write @ drivers/nvs/nvs.c:144
nvs_read @ drivers/nvs/nvs.c:55
nvs_read @ drivers/nvs/nvs.c:69
threewire_detect_address_len @ drivers/nvs/threewire.c:121
FD_SET @ include/gpxe/posix_io.h:49
FD_ISSET @ include/gpxe/posix_io.h:72
net80211_process_ie @ net/80211/net80211.c:1075
net80211_marshal_request_info @ net/80211/net80211.c:1207
net80211_set_rtscts_rate @ net/80211/net80211.c:1904
rc80211_calc_net_goodness @ net/80211/rc80211.c:177