LCOV - code coverage report
Current view: top level - src - simpletlv.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 124 136 91.2 %
Date: 2023-06-12 11:15:51 Functions: 11 11 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 73 94 77.7 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * simpletlv.c: Simple TLV encoding and decoding functions
       3                 :            :  *
       4                 :            :  * Copyright (C) 2016 - 2018 Red Hat, Inc.
       5                 :            :  *
       6                 :            :  * Authors: Robert Relyea <rrelyea@redhat.com>
       7                 :            :  *          Jakub Jelen <jjelen@redhat.com>
       8                 :            :  *
       9                 :            :  * This library is free software; you can redistribute it and/or
      10                 :            :  * modify it under the terms of the GNU Lesser General Public
      11                 :            :  * License as published by the Free Software Foundation; either
      12                 :            :  * version 2.1 of the License, or (at your option) any later version.
      13                 :            :  *
      14                 :            :  * This library is distributed in the hope that it will be useful,
      15                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      17                 :            :  * Lesser General Public License for more details.
      18                 :            :  *
      19                 :            :  * You should have received a copy of the GNU Lesser General Public
      20                 :            :  * License along with this library; if not, write to the Free Software
      21                 :            :  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      22                 :            :  */
      23                 :            : 
      24                 :            : #include "config.h"
      25                 :            : 
      26                 :            : #include <glib.h>
      27                 :            : #include <stdio.h>
      28                 :            : #include <string.h>
      29                 :            : #include <ctype.h>
      30                 :            : #include <stdlib.h>
      31                 :            : 
      32                 :            : #include "simpletlv.h"
      33                 :            : #include "common.h"
      34                 :            : 
      35                 :            : int
      36                 :        931 : simpletlv_get_length(struct simpletlv_member *tlv, size_t tlv_len,
      37                 :            :                      enum simpletlv_buffer_type buffer_type)
      38                 :            : {
      39                 :            :     size_t i, len = 0;
      40                 :            :     int child_length;
      41                 :            : 
      42         [ +  + ]:       5863 :     for (i = 0; i < tlv_len; i++) {
      43                 :            :         /* This TLV is skipped */
      44         [ +  + ]:       4936 :         if (tlv[i].type == SIMPLETLV_TYPE_NONE)
      45                 :        182 :             continue;
      46                 :            : 
      47                 :            :         /* We can not unambiguously split the buffers
      48                 :            :          * for recursive structures
      49                 :            :          */
      50         [ +  + ]:       4754 :         if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND
      51         [ +  + ]:        152 :             && buffer_type != SIMPLETLV_BOTH)
      52                 :            :             return -1;
      53                 :            : 
      54                 :       4750 :         child_length = tlv[i].length;
      55         [ +  + ]:       4750 :         if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
      56                 :        148 :             child_length = simpletlv_get_length(tlv[i].value.child,
      57                 :            :                 tlv[i].length, SIMPLETLV_BOTH);
      58                 :            :         }
      59         [ +  + ]:       4750 :         if (buffer_type & SIMPLETLV_TL) {
      60                 :            :             len += 1/*TAG*/;
      61         [ +  + ]:       4307 :             if (child_length < 255)
      62                 :       4271 :                 len += 1;
      63                 :            :             else
      64                 :         36 :                 len += 3;
      65                 :            :         }
      66         [ +  + ]:       4750 :         if (buffer_type & SIMPLETLV_VALUE) {
      67                 :       4308 :             len += child_length;
      68                 :            :         }
      69                 :            :     }
      70                 :        927 :     return len;
      71                 :            : }
      72                 :            : 
      73                 :            : static int
      74                 :        581 : simpletlv_encode_internal(struct simpletlv_member *tlv, size_t tlv_len,
      75                 :            :                           unsigned char **out, size_t outlen,
      76                 :            :                           unsigned char **newptr, int buffer_type)
      77                 :            : {
      78                 :        581 :     unsigned char *tmp = NULL, *a = NULL, *p = NULL, *newp = NULL;
      79                 :            :     size_t tmp_len = 0, p_len, i;
      80                 :            :     int expect_len = 0, rv;
      81                 :            : 
      82                 :        581 :     expect_len = simpletlv_get_length(tlv, tlv_len, buffer_type);
      83   [ +  +  +  - ]:        581 :     if (expect_len == 0 && newptr != NULL && out != NULL)
      84                 :          1 :         *newptr = *out; /* Corner case for zero-length values */
      85         [ +  + ]:        581 :     if (expect_len <= 0)
      86                 :            :         return expect_len;
      87                 :            : 
      88         [ +  + ]:        577 :     if (outlen == 0 && out != NULL) {
      89                 :            :         /* allocate a new buffer */
      90                 :        380 :         a = g_malloc(expect_len);
      91                 :            :         tmp = a;
      92                 :            :         tmp_len = expect_len;
      93   [ +  +  +  - ]:        197 :     } else if ((int)outlen >= expect_len && out != NULL) {
      94                 :        194 :         tmp = *out;
      95                 :        194 :         tmp_len = outlen;
      96                 :            :     } else {
      97                 :            :         /* we can not fit the data */
      98                 :            :         return -1;
      99                 :            :     }
     100                 :        574 :     p = tmp;
     101                 :            :     p_len = tmp_len;
     102         [ +  + ]:       3950 :     for (i = 0; i < tlv_len; i++) {
     103                 :       3376 :         size_t child_length = tlv[i].length;
     104                 :            : 
     105                 :            :         /* This TLV is skipped */
     106         [ +  + ]:       3376 :         if (tlv[i].type == SIMPLETLV_TYPE_NONE)
     107                 :         91 :             continue;
     108                 :            : 
     109         [ +  + ]:       3285 :         if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
     110                 :        146 :             child_length = simpletlv_get_length(tlv[i].value.child,
     111                 :            :                 tlv[i].length, SIMPLETLV_BOTH);
     112                 :            :         }
     113         [ +  + ]:       3285 :         if (buffer_type & SIMPLETLV_TL) {
     114                 :       3065 :             rv = simpletlv_put_tag(tlv[i].tag, child_length,
     115                 :            :                 p, p_len, &newp);
     116         [ -  + ]:       3065 :             if (rv < 0)
     117                 :          0 :                 goto failure;
     118                 :       3065 :             p = newp;
     119                 :            :         }
     120         [ +  + ]:       3285 :         if (buffer_type & SIMPLETLV_VALUE) {
     121         [ +  + ]:       3064 :             if (tlv[i].type == SIMPLETLV_TYPE_LEAF) {
     122                 :       2918 :                 memcpy(p, tlv[i].value.value, tlv[i].length);
     123                 :       2918 :                 p += tlv[i].length;
     124                 :            :             } else {
     125                 :            :                 /* recurse */
     126                 :        146 :                 rv = simpletlv_encode_internal(tlv[i].value.child,
     127                 :        146 :                     tlv[i].length, &p, p_len, &newp, buffer_type);
     128         [ -  + ]:        146 :                 if (rv < 0)
     129                 :          0 :                     goto failure;
     130                 :        146 :                 p = newp;
     131                 :            :             }
     132                 :            :         }
     133                 :       3285 :         p_len = tmp_len - (p - tmp);
     134                 :            :     }
     135         [ +  + ]:        574 :     if (newptr)
     136                 :        146 :         *newptr = p;
     137                 :            :     if (out)
     138                 :        574 :         *out = tmp;
     139                 :        574 :     return tmp_len - p_len;
     140                 :            : 
     141                 :          0 : failure:
     142                 :          0 :     g_free(a);
     143                 :          0 :     return -1;
     144                 :            : }
     145                 :            : 
     146                 :            : int
     147                 :        382 : simpletlv_encode(struct simpletlv_member *tlv, size_t tlv_len,
     148                 :            :                  unsigned char **out, size_t outlen, unsigned char **newptr)
     149                 :            : {
     150                 :        382 :     return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
     151                 :            :         SIMPLETLV_BOTH);
     152                 :            : }
     153                 :            : 
     154                 :            : int
     155                 :         26 : simpletlv_encode_tl(struct simpletlv_member *tlv, size_t tlv_len,
     156                 :            :                     unsigned char **out, size_t outlen, unsigned char **newptr)
     157                 :            : {
     158                 :         26 :     return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
     159                 :            :         SIMPLETLV_TL);
     160                 :            : }
     161                 :            : 
     162                 :            : int
     163                 :         27 : simpletlv_encode_val(struct simpletlv_member *tlv, size_t tlv_len,
     164                 :            :                      unsigned char **out, size_t outlen, unsigned char **newptr)
     165                 :            : {
     166                 :         27 :     return simpletlv_encode_internal(tlv, tlv_len, out, outlen, newptr,
     167                 :            :         SIMPLETLV_VALUE);
     168                 :            : }
     169                 :            : 
     170                 :            : 
     171                 :            : /*
     172                 :            :  * Put a tag/length record to a file in Simple TLV based on the  datalen
     173                 :            :  * content length.
     174                 :            :  */
     175                 :            : int
     176                 :       3065 : simpletlv_put_tag(unsigned char tag, size_t datalen, unsigned char *out,
     177                 :            :                   size_t outlen, unsigned char **ptr)
     178                 :            : {
     179                 :            :     unsigned char *p = out;
     180                 :            : 
     181   [ +  -  -  + ]:       3065 :     if (outlen < 2 || (outlen < 4 && datalen >= 0xff))
     182                 :            :         return -1;
     183                 :            : 
     184                 :            :     /* tag is just number between 0x01 and 0xFE */
     185         [ -  + ]:       3065 :     if (tag == 0x00 || tag == 0xff)
     186                 :            :         return -1;
     187                 :            : 
     188                 :       3065 :     *p++ = tag; /* tag is single byte */
     189         [ +  + ]:       3065 :     if (datalen < 0xff) {
     190                 :            :         /* short value up to 255 */
     191                 :       3047 :         *p++ = (unsigned char)datalen; /* is in the second byte */
     192         [ -  + ]:         18 :     } else if (datalen < 0xffff) {
     193                 :            :         /* longer values up to 65535 */
     194                 :         18 :         *p++ = (unsigned char)0xff; /* first byte is 0xff */
     195                 :         18 :         *p++ = (unsigned char)datalen & 0xff;
     196                 :         18 :         *p++ = (unsigned char)(datalen >> 8) & 0xff; /* LE */
     197                 :            :     } else {
     198                 :            :         /* we can't store more than two bytes in Simple TLV */
     199                 :            :         return -1;
     200                 :            :     }
     201         [ +  - ]:       3065 :     if (ptr != NULL)
     202                 :       3065 :         *ptr = p;
     203                 :            :     return 0;
     204                 :            : }
     205                 :            : 
     206                 :            : /* Read the TL file and return appropriate tag and the length of associated
     207                 :            :  * content.
     208                 :            :  */
     209                 :            : int
     210                 :        847 : simpletlv_read_tag(unsigned char **buf, size_t buflen, unsigned char *tag_out,
     211                 :            :                    size_t *taglen)
     212                 :            : {
     213                 :            :     size_t len;
     214                 :        847 :     unsigned char *p = *buf;
     215                 :            : 
     216         [ +  + ]:        847 :     if (buflen < 2) {
     217                 :          1 :         *buf = p+buflen;
     218                 :          1 :         return -1;
     219                 :            :     }
     220                 :            : 
     221                 :        846 :     *tag_out = *p++;
     222                 :        846 :     len = *p++;
     223         [ +  + ]:        846 :     if (len == 0xff) {
     224                 :            :         /* don't crash on bad data */
     225         [ +  + ]:          3 :         if (buflen < 4) {
     226                 :          1 :             *taglen = 0;
     227                 :          1 :             return -1;
     228                 :            :         }
     229                 :            :         /* skip two bytes (the size) */
     230                 :          2 :         len = lebytes2ushort(p);
     231                 :          2 :         p+=2;
     232                 :            :     }
     233                 :        845 :     *taglen = len;
     234                 :        845 :     *buf = p;
     235                 :        845 :     return 0;
     236                 :            : }
     237                 :            : 
     238                 :            : /*
     239                 :            :  * Merges two structures into one, creating a new shallow copy of both
     240                 :            :  * of the structures.
     241                 :            :  * Resulting length is the sum of  a_len  and  b_len  arguments.
     242                 :            :  */
     243                 :            : struct simpletlv_member *
     244                 :         93 : simpletlv_merge(const struct simpletlv_member *a, size_t a_len,
     245                 :            :                 const struct simpletlv_member *b, size_t b_len)
     246                 :            : {
     247                 :            :     int offset;
     248                 :            :     struct simpletlv_member *r;
     249                 :         93 :     size_t r_len = a_len + b_len;
     250                 :            : 
     251   [ -  +  -  - ]:         93 :     r = g_new(struct simpletlv_member, r_len);
     252                 :            : 
     253                 :            :     /* the ugly way */
     254                 :         93 :     offset = a_len * sizeof(struct simpletlv_member);
     255                 :         93 :     memcpy(r, a, offset);
     256                 :         93 :     memcpy(&r[a_len], b, b_len * sizeof(struct simpletlv_member));
     257                 :         93 :     return r;
     258                 :            : }
     259                 :            : 
     260                 :            : void
     261                 :        287 : simpletlv_free(struct simpletlv_member *tlv, size_t tlvlen)
     262                 :            : {
     263                 :            :     size_t i;
     264         [ +  - ]:        287 :     if (tlv == NULL)
     265                 :            :         return;
     266                 :            : 
     267         [ +  + ]:       2829 :     for (i = 0; i < tlvlen; i++) {
     268         [ +  + ]:       2542 :         if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
     269                 :         68 :             simpletlv_free(tlv[i].value.child, tlv[i].length);
     270                 :            :         } else {
     271                 :       2474 :             g_free(tlv[i].value.value);
     272                 :            :         }
     273                 :            :     }
     274                 :        287 :     g_free(tlv);
     275                 :            : }
     276                 :            : 
     277                 :            : struct simpletlv_member *
     278                 :        219 : simpletlv_clone(struct simpletlv_member *tlv, size_t tlvlen)
     279                 :            : {
     280                 :            :     size_t i = 0, j;
     281                 :            :     struct simpletlv_member *new = NULL;
     282                 :            : 
     283   [ -  +  -  - ]:        219 :     new = g_new(struct simpletlv_member, tlvlen);
     284                 :            : 
     285         [ +  + ]:        923 :     for (i = 0; i < tlvlen; i++) {
     286                 :        704 :         new[i].type = tlv[i].type;
     287                 :        704 :         new[i].tag = tlv[i].tag;
     288                 :        704 :         new[i].length = tlv[i].length;
     289         [ +  + ]:        704 :         if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
     290                 :         82 :             new[i].value.child = simpletlv_clone(
     291                 :            :                 tlv[i].value.child, tlv[i].length);
     292         [ -  + ]:         82 :             if (new[i].value.child == NULL)
     293                 :          0 :                 goto failure;
     294                 :            :         } else {
     295                 :        622 :             new[i].value.value = g_memdup2(tlv[i].value.value, tlv[i].length);
     296                 :            :         }
     297                 :            :     }
     298                 :            :     return new;
     299                 :            : 
     300                 :            : failure:
     301         [ #  # ]:          0 :     for (j = 0; j < i; j++) {
     302         [ #  # ]:          0 :         if (tlv[i].type == SIMPLETLV_TYPE_COMPOUND) {
     303                 :          0 :             simpletlv_free(new[j].value.child, new[j].length);
     304                 :            :         } else {
     305                 :          0 :             g_free(new[j].value.value);
     306                 :            :         }
     307                 :            :     }
     308                 :          0 :     g_free(new);
     309                 :          0 :     return NULL;
     310                 :            : }
     311                 :            : 
     312                 :            : struct simpletlv_member *
     313                 :          5 : simpletlv_parse(unsigned char *data, size_t data_len, size_t *outtlv_len)
     314                 :            : {
     315                 :            :     unsigned char *p, *p_end;
     316                 :            :     unsigned char tag;
     317                 :            :     size_t vlen;
     318                 :          5 :     GArray *tlv = g_array_new(FALSE, FALSE, sizeof(struct simpletlv_member));
     319                 :            : 
     320                 :          5 :     p = data;
     321                 :          5 :     p_end = p + data_len;
     322         [ +  + ]:        521 :     while (p < p_end) {
     323                 :            :         struct simpletlv_member tlvp;
     324                 :            : 
     325                 :            :         /* we can return what was parsed successfully */
     326         [ +  + ]:        519 :         if (simpletlv_read_tag(&p, p_end - p, &tag, &vlen) < 0) {
     327                 :            :             break;
     328                 :            :         }
     329         [ +  + ]:        517 :         if (vlen > (size_t) (p_end - p)) {
     330                 :            :             break;
     331                 :            :         }
     332                 :            : 
     333                 :        516 :         tlvp.tag = tag;
     334                 :        516 :         tlvp.length = vlen;
     335                 :        516 :         tlvp.value.value = g_memdup2(p, vlen);
     336                 :        516 :         tlvp.type = SIMPLETLV_TYPE_LEAF;
     337                 :        516 :         g_array_append_val(tlv, tlvp);
     338                 :            : 
     339                 :        516 :         p += vlen;
     340                 :            :     }
     341                 :            : 
     342                 :          5 :     *outtlv_len = tlv->len;
     343                 :          5 :     return (struct simpletlv_member *)(void *)g_array_free(tlv, FALSE);
     344                 :            : }
     345                 :            : 
     346                 :            : /* vim: set ts=4 sw=4 tw=0 noet expandtab: */

Generated by: LCOV version 1.14