LCOV - code coverage report
Current view: top level - lib - hexdump.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 88 0.0 %
Date: 2023-04-06 08:38:28 Functions: 0 5 0.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-only
       2             : /*
       3             :  * lib/hexdump.c
       4             :  */
       5             : 
       6             : #include <linux/types.h>
       7             : #include <linux/ctype.h>
       8             : #include <linux/errno.h>
       9             : #include <linux/kernel.h>
      10             : #include <linux/minmax.h>
      11             : #include <linux/export.h>
      12             : #include <asm/unaligned.h>
      13             : 
      14             : const char hex_asc[] = "0123456789abcdef";
      15             : EXPORT_SYMBOL(hex_asc);
      16             : const char hex_asc_upper[] = "0123456789ABCDEF";
      17             : EXPORT_SYMBOL(hex_asc_upper);
      18             : 
      19             : /**
      20             :  * hex_to_bin - convert a hex digit to its real value
      21             :  * @ch: ascii character represents hex digit
      22             :  *
      23             :  * hex_to_bin() converts one hex digit to its actual value or -1 in case of bad
      24             :  * input.
      25             :  *
      26             :  * This function is used to load cryptographic keys, so it is coded in such a
      27             :  * way that there are no conditions or memory accesses that depend on data.
      28             :  *
      29             :  * Explanation of the logic:
      30             :  * (ch - '9' - 1) is negative if ch <= '9'
      31             :  * ('0' - 1 - ch) is negative if ch >= '0'
      32             :  * we "and" these two values, so the result is negative if ch is in the range
      33             :  *      '0' ... '9'
      34             :  * we are only interested in the sign, so we do a shift ">> 8"; note that right
      35             :  *      shift of a negative value is implementation-defined, so we cast the
      36             :  *      value to (unsigned) before the shift --- we have 0xffffff if ch is in
      37             :  *      the range '0' ... '9', 0 otherwise
      38             :  * we "and" this value with (ch - '0' + 1) --- we have a value 1 ... 10 if ch is
      39             :  *      in the range '0' ... '9', 0 otherwise
      40             :  * we add this value to -1 --- we have a value 0 ... 9 if ch is in the range '0'
      41             :  *      ... '9', -1 otherwise
      42             :  * the next line is similar to the previous one, but we need to decode both
      43             :  *      uppercase and lowercase letters, so we use (ch & 0xdf), which converts
      44             :  *      lowercase to uppercase
      45             :  */
      46           0 : int hex_to_bin(unsigned char ch)
      47             : {
      48           0 :         unsigned char cu = ch & 0xdf;
      49             :         return -1 +
      50           0 :                 ((ch - '0' +  1) & (unsigned)((ch - '9' - 1) & ('0' - 1 - ch)) >> 8) +
      51           0 :                 ((cu - 'A' + 11) & (unsigned)((cu - 'F' - 1) & ('A' - 1 - cu)) >> 8);
      52             : }
      53             : EXPORT_SYMBOL(hex_to_bin);
      54             : 
      55             : /**
      56             :  * hex2bin - convert an ascii hexadecimal string to its binary representation
      57             :  * @dst: binary result
      58             :  * @src: ascii hexadecimal string
      59             :  * @count: result length
      60             :  *
      61             :  * Return 0 on success, -EINVAL in case of bad input.
      62             :  */
      63           0 : int hex2bin(u8 *dst, const char *src, size_t count)
      64             : {
      65           0 :         while (count--) {
      66             :                 int hi, lo;
      67             : 
      68           0 :                 hi = hex_to_bin(*src++);
      69           0 :                 if (unlikely(hi < 0))
      70             :                         return -EINVAL;
      71           0 :                 lo = hex_to_bin(*src++);
      72           0 :                 if (unlikely(lo < 0))
      73             :                         return -EINVAL;
      74             : 
      75           0 :                 *dst++ = (hi << 4) | lo;
      76             :         }
      77             :         return 0;
      78             : }
      79             : EXPORT_SYMBOL(hex2bin);
      80             : 
      81             : /**
      82             :  * bin2hex - convert binary data to an ascii hexadecimal string
      83             :  * @dst: ascii hexadecimal result
      84             :  * @src: binary data
      85             :  * @count: binary data length
      86             :  */
      87           0 : char *bin2hex(char *dst, const void *src, size_t count)
      88             : {
      89           0 :         const unsigned char *_src = src;
      90             : 
      91           0 :         while (count--)
      92           0 :                 dst = hex_byte_pack(dst, *_src++);
      93           0 :         return dst;
      94             : }
      95             : EXPORT_SYMBOL(bin2hex);
      96             : 
      97             : /**
      98             :  * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory
      99             :  * @buf: data blob to dump
     100             :  * @len: number of bytes in the @buf
     101             :  * @rowsize: number of bytes to print per line; must be 16 or 32
     102             :  * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
     103             :  * @linebuf: where to put the converted data
     104             :  * @linebuflen: total size of @linebuf, including space for terminating NUL
     105             :  * @ascii: include ASCII after the hex output
     106             :  *
     107             :  * hex_dump_to_buffer() works on one "line" of output at a time, i.e.,
     108             :  * 16 or 32 bytes of input data converted to hex + ASCII output.
     109             :  *
     110             :  * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data
     111             :  * to a hex + ASCII dump at the supplied memory location.
     112             :  * The converted output is always NUL-terminated.
     113             :  *
     114             :  * E.g.:
     115             :  *   hex_dump_to_buffer(frame->data, frame->len, 16, 1,
     116             :  *                      linebuf, sizeof(linebuf), true);
     117             :  *
     118             :  * example output buffer:
     119             :  * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO
     120             :  *
     121             :  * Return:
     122             :  * The amount of bytes placed in the buffer without terminating NUL. If the
     123             :  * output was truncated, then the return value is the number of bytes
     124             :  * (excluding the terminating NUL) which would have been written to the final
     125             :  * string if enough space had been available.
     126             :  */
     127           0 : int hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int groupsize,
     128             :                        char *linebuf, size_t linebuflen, bool ascii)
     129             : {
     130           0 :         const u8 *ptr = buf;
     131             :         int ngroups;
     132             :         u8 ch;
     133           0 :         int j, lx = 0;
     134             :         int ascii_column;
     135             :         int ret;
     136             : 
     137           0 :         if (rowsize != 16 && rowsize != 32)
     138           0 :                 rowsize = 16;
     139             : 
     140           0 :         if (len > rowsize)           /* limit to one line at a time */
     141           0 :                 len = rowsize;
     142           0 :         if (!is_power_of_2(groupsize) || groupsize > 8)
     143           0 :                 groupsize = 1;
     144           0 :         if ((len % groupsize) != 0)     /* no mixed size output */
     145           0 :                 groupsize = 1;
     146             : 
     147           0 :         ngroups = len / groupsize;
     148           0 :         ascii_column = rowsize * 2 + rowsize / groupsize + 1;
     149             : 
     150           0 :         if (!linebuflen)
     151             :                 goto overflow1;
     152             : 
     153           0 :         if (!len)
     154             :                 goto nil;
     155             : 
     156           0 :         if (groupsize == 8) {
     157             :                 const u64 *ptr8 = buf;
     158             : 
     159           0 :                 for (j = 0; j < ngroups; j++) {
     160           0 :                         ret = snprintf(linebuf + lx, linebuflen - lx,
     161             :                                        "%s%16.16llx", j ? " " : "",
     162           0 :                                        get_unaligned(ptr8 + j));
     163           0 :                         if (ret >= linebuflen - lx)
     164             :                                 goto overflow1;
     165           0 :                         lx += ret;
     166             :                 }
     167           0 :         } else if (groupsize == 4) {
     168             :                 const u32 *ptr4 = buf;
     169             : 
     170           0 :                 for (j = 0; j < ngroups; j++) {
     171           0 :                         ret = snprintf(linebuf + lx, linebuflen - lx,
     172             :                                        "%s%8.8x", j ? " " : "",
     173           0 :                                        get_unaligned(ptr4 + j));
     174           0 :                         if (ret >= linebuflen - lx)
     175             :                                 goto overflow1;
     176           0 :                         lx += ret;
     177             :                 }
     178           0 :         } else if (groupsize == 2) {
     179             :                 const u16 *ptr2 = buf;
     180             : 
     181           0 :                 for (j = 0; j < ngroups; j++) {
     182           0 :                         ret = snprintf(linebuf + lx, linebuflen - lx,
     183             :                                        "%s%4.4x", j ? " " : "",
     184           0 :                                        get_unaligned(ptr2 + j));
     185           0 :                         if (ret >= linebuflen - lx)
     186             :                                 goto overflow1;
     187           0 :                         lx += ret;
     188             :                 }
     189             :         } else {
     190           0 :                 for (j = 0; j < len; j++) {
     191           0 :                         if (linebuflen < lx + 2)
     192             :                                 goto overflow2;
     193           0 :                         ch = ptr[j];
     194           0 :                         linebuf[lx++] = hex_asc_hi(ch);
     195           0 :                         if (linebuflen < lx + 2)
     196             :                                 goto overflow2;
     197           0 :                         linebuf[lx++] = hex_asc_lo(ch);
     198           0 :                         if (linebuflen < lx + 2)
     199             :                                 goto overflow2;
     200           0 :                         linebuf[lx++] = ' ';
     201             :                 }
     202           0 :                 if (j)
     203           0 :                         lx--;
     204             :         }
     205           0 :         if (!ascii)
     206             :                 goto nil;
     207             : 
     208           0 :         while (lx < ascii_column) {
     209           0 :                 if (linebuflen < lx + 2)
     210             :                         goto overflow2;
     211           0 :                 linebuf[lx++] = ' ';
     212             :         }
     213           0 :         for (j = 0; j < len; j++) {
     214           0 :                 if (linebuflen < lx + 2)
     215             :                         goto overflow2;
     216           0 :                 ch = ptr[j];
     217           0 :                 linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';
     218             :         }
     219             : nil:
     220           0 :         linebuf[lx] = '\0';
     221           0 :         return lx;
     222             : overflow2:
     223           0 :         linebuf[lx++] = '\0';
     224             : overflow1:
     225           0 :         return ascii ? ascii_column + len : (groupsize * 2 + 1) * ngroups - 1;
     226             : }
     227             : EXPORT_SYMBOL(hex_dump_to_buffer);
     228             : 
     229             : #ifdef CONFIG_PRINTK
     230             : /**
     231             :  * print_hex_dump - print a text hex dump to syslog for a binary blob of data
     232             :  * @level: kernel log level (e.g. KERN_DEBUG)
     233             :  * @prefix_str: string to prefix each line with;
     234             :  *  caller supplies trailing spaces for alignment if desired
     235             :  * @prefix_type: controls whether prefix of an offset, address, or none
     236             :  *  is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
     237             :  * @rowsize: number of bytes to print per line; must be 16 or 32
     238             :  * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
     239             :  * @buf: data blob to dump
     240             :  * @len: number of bytes in the @buf
     241             :  * @ascii: include ASCII after the hex output
     242             :  *
     243             :  * Given a buffer of u8 data, print_hex_dump() prints a hex + ASCII dump
     244             :  * to the kernel log at the specified kernel log level, with an optional
     245             :  * leading prefix.
     246             :  *
     247             :  * print_hex_dump() works on one "line" of output at a time, i.e.,
     248             :  * 16 or 32 bytes of input data converted to hex + ASCII output.
     249             :  * print_hex_dump() iterates over the entire input @buf, breaking it into
     250             :  * "line size" chunks to format and print.
     251             :  *
     252             :  * E.g.:
     253             :  *   print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS,
     254             :  *                  16, 1, frame->data, frame->len, true);
     255             :  *
     256             :  * Example output using %DUMP_PREFIX_OFFSET and 1-byte mode:
     257             :  * 0009ab42: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO
     258             :  * Example output using %DUMP_PREFIX_ADDRESS and 4-byte mode:
     259             :  * ffffffff88089af0: 73727170 77767574 7b7a7978 7f7e7d7c  pqrstuvwxyz{|}~.
     260             :  */
     261           0 : void print_hex_dump(const char *level, const char *prefix_str, int prefix_type,
     262             :                     int rowsize, int groupsize,
     263             :                     const void *buf, size_t len, bool ascii)
     264             : {
     265           0 :         const u8 *ptr = buf;
     266           0 :         int i, linelen, remaining = len;
     267             :         unsigned char linebuf[32 * 3 + 2 + 32 + 1];
     268             : 
     269           0 :         if (rowsize != 16 && rowsize != 32)
     270           0 :                 rowsize = 16;
     271             : 
     272           0 :         for (i = 0; i < len; i += rowsize) {
     273           0 :                 linelen = min(remaining, rowsize);
     274           0 :                 remaining -= rowsize;
     275             : 
     276           0 :                 hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
     277             :                                    linebuf, sizeof(linebuf), ascii);
     278             : 
     279           0 :                 switch (prefix_type) {
     280             :                 case DUMP_PREFIX_ADDRESS:
     281           0 :                         printk("%s%s%p: %s\n",
     282             :                                level, prefix_str, ptr + i, linebuf);
     283           0 :                         break;
     284             :                 case DUMP_PREFIX_OFFSET:
     285           0 :                         printk("%s%s%.8x: %s\n", level, prefix_str, i, linebuf);
     286           0 :                         break;
     287             :                 default:
     288           0 :                         printk("%s%s%s\n", level, prefix_str, linebuf);
     289           0 :                         break;
     290             :                 }
     291             :         }
     292           0 : }
     293             : EXPORT_SYMBOL(print_hex_dump);
     294             : 
     295             : #endif /* defined(CONFIG_PRINTK) */

Generated by: LCOV version 1.14