xref: /openbmc/libbej/src/bej_dictionary.c (revision 8c4332a53d8fee66f0cfd8680f9254e02fcb6053)
1 #include "bej_dictionary.h"
2 
3 #include <stdbool.h>
4 #include <stddef.h>
5 #include <stdio.h>
6 #include <string.h>
7 
8 /**
9  * @brief Get the index for a property offset. First property will be at index
10  * 0.
11  *
12  * @param[in] propertyOffset - a valid property offset.
13  * @return index of the property.
14  */
bejGetPropertyEntryIndex(uint16_t propertyOffset)15 static uint16_t bejGetPropertyEntryIndex(uint16_t propertyOffset)
16 {
17     return (propertyOffset - bejDictGetPropertyHeadOffset()) /
18            sizeof(struct BejDictionaryProperty);
19 }
20 
21 /**
22  * @brief  Validate a property offset.
23  *
24  * @param[in] dictionary - pointer to the dictionary.
25  * @param[in] propertyOffset - offset needed to be validated.
26  * @return true if propertyOffset is a valid offset.
27  */
bejValidatePropertyOffset(const uint8_t * dictionary,uint16_t propertyOffset)28 static bool bejValidatePropertyOffset(const uint8_t* dictionary,
29                                       uint16_t propertyOffset)
30 {
31     // propertyOffset should be greater than or equal to first property offset.
32     if (propertyOffset < bejDictGetPropertyHeadOffset())
33     {
34         fprintf(
35             stderr,
36             "Invalid property offset. Pointing to Dictionary header data\n");
37         return false;
38     }
39 
40     // propertyOffset should be a multiple of sizeof(BejDictionaryProperty)
41     // starting from first property within the dictionary.
42     if ((propertyOffset - bejDictGetPropertyHeadOffset()) %
43         sizeof(struct BejDictionaryProperty))
44     {
45         fprintf(stderr, "Invalid property offset. Does not point to beginning "
46                         "of property\n");
47         return false;
48     }
49 
50     const struct BejDictionaryHeader* header =
51         (const struct BejDictionaryHeader*)dictionary;
52     uint16_t propertyIndex = bejGetPropertyEntryIndex(propertyOffset);
53     if (propertyIndex >= header->entryCount)
54     {
55         fprintf(stderr,
56                 "Invalid property offset %u. It falls outside of dictionary "
57                 "properties\n",
58                 propertyOffset);
59         return false;
60     }
61 
62     return true;
63 }
64 
bejValidatePropertyNameLength(const uint8_t * dictionary,uint32_t dictionarySize,uint16_t nameOffset,uint8_t nameLength)65 static bool bejValidatePropertyNameLength(
66     const uint8_t* dictionary, uint32_t dictionarySize, uint16_t nameOffset,
67     uint8_t nameLength)
68 {
69     if (nameLength == 0)
70     {
71         return true;
72     }
73     // After the property names, the dictionary has at least the
74     // uint8 CopyrightLength field. So we compare with dictionarySize - 1.
75     // nameOffset is uint16_t and nameLength is uint8_t so it cannot overflow
76     // uint32_t dictionarySize here.
77     if (((uint32_t)nameOffset) + (uint32_t)(nameLength) > (dictionarySize - 1))
78     {
79         fprintf(stderr, "Property name length goes beyond dictionary size\n");
80         return false;
81     }
82 
83     // nameLength includes the NULL terminating. So the actual string size
84     // should be nameLength - 1.
85     const char* name = (const char*)dictionary + nameOffset;
86     const void* nullTerminator = memchr(name, '\0', nameLength);
87     if (nullTerminator == NULL ||
88         ((const char*)nullTerminator - name) != (ptrdiff_t)(nameLength - 1))
89     {
90         fprintf(stderr, "Property name length doesn't match actual length\n");
91         return false;
92     }
93     return true;
94 }
95 
bejDictGetPropertyHeadOffset(void)96 uint16_t bejDictGetPropertyHeadOffset(void)
97 {
98     // First property is present soon after the dictionary header.
99     return sizeof(struct BejDictionaryHeader);
100 }
101 
bejDictGetFirstAnnotatedPropertyOffset(void)102 uint16_t bejDictGetFirstAnnotatedPropertyOffset(void)
103 {
104     // The first property available is the "Annotations" set which is the parent
105     // for all properties. Next immediate property is the first property we
106     // need.
107     return sizeof(struct BejDictionaryHeader) +
108            sizeof(struct BejDictionaryProperty);
109 }
110 
bejDictGetProperty(const uint8_t * dictionary,uint16_t startingPropertyOffset,uint16_t sequenceNumber,const struct BejDictionaryProperty ** property)111 int bejDictGetProperty(const uint8_t* dictionary,
112                        uint16_t startingPropertyOffset, uint16_t sequenceNumber,
113                        const struct BejDictionaryProperty** property)
114 {
115     uint16_t propertyOffset = startingPropertyOffset;
116     const struct BejDictionaryHeader* header =
117         (const struct BejDictionaryHeader*)dictionary;
118 
119     if (!bejValidatePropertyOffset(dictionary, propertyOffset))
120     {
121         return bejErrorInvalidPropertyOffset;
122     }
123     uint16_t propertyIndex = bejGetPropertyEntryIndex(propertyOffset);
124 
125     for (uint16_t index = propertyIndex; index < header->entryCount; ++index)
126     {
127         const struct BejDictionaryProperty* p =
128             (const struct BejDictionaryProperty*)(dictionary + propertyOffset);
129         if (p->sequenceNumber == sequenceNumber)
130         {
131             if (!bejValidatePropertyNameLength(dictionary,
132                                                header->dictionarySize,
133                                                p->nameOffset, p->nameLength))
134             {
135                 return bejErrorInvalidSize;
136             }
137 
138             *property = p;
139             return 0;
140         }
141         propertyOffset += sizeof(struct BejDictionaryProperty);
142     }
143     return bejErrorUnknownProperty;
144 }
145 
bejDictGetPropertyName(const uint8_t * dictionary,uint16_t nameOffset,uint8_t nameLength)146 const char* bejDictGetPropertyName(const uint8_t* dictionary,
147                                    uint16_t nameOffset, uint8_t nameLength)
148 {
149     if (nameLength == 0)
150     {
151         return "";
152     }
153     return (const char*)(dictionary + nameOffset);
154 }
155 
bejDictGetPropertyByName(const uint8_t * dictionary,uint16_t startingPropertyOffset,const char * propertyName,const struct BejDictionaryProperty ** property,uint16_t * propertyOffset)156 int bejDictGetPropertyByName(
157     const uint8_t* dictionary, uint16_t startingPropertyOffset,
158     const char* propertyName, const struct BejDictionaryProperty** property,
159     uint16_t* propertyOffset)
160 {
161     NULL_CHECK(property, "property in bejDictGetPropertyByName");
162 
163     uint16_t curPropertyOffset = startingPropertyOffset;
164     const struct BejDictionaryHeader* header =
165         (const struct BejDictionaryHeader*)dictionary;
166 
167     if (!bejValidatePropertyOffset(dictionary, curPropertyOffset))
168     {
169         return bejErrorInvalidPropertyOffset;
170     }
171     uint16_t propertyIndex = bejGetPropertyEntryIndex(curPropertyOffset);
172 
173     for (uint16_t index = propertyIndex; index < header->entryCount; ++index)
174     {
175         const struct BejDictionaryProperty* p =
176             (const struct BejDictionaryProperty*)(dictionary +
177                                                   curPropertyOffset);
178         if (strcmp(propertyName,
179                    bejDictGetPropertyName(dictionary, p->nameOffset,
180                                           p->nameLength)) == 0)
181         {
182             *property = p;
183             // propertyOffset is an optional output.
184             if (propertyOffset != NULL)
185             {
186                 *propertyOffset = curPropertyOffset;
187             }
188             return 0;
189         }
190         curPropertyOffset += sizeof(struct BejDictionaryProperty);
191     }
192     return bejErrorUnknownProperty;
193 }
194