CVE-2022-1987: Out-of-bounds Read (CWE-125)
value: Buffer Over-read in GitHub repository bfabiszewski/libmobi prior to 0.11.
CWE description: The product reads data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 8.1
Published: 2022-06-03T08:15Z
Repository: https://github.com/bfabiszewski/libmobi
Language: Other
Files changed: ChangeLog, src/index.c
Commit date: 2022-05-05 20:54:11 +0200
Commit message: Fix: index entry label not being zero-terminated with corrupt input

Diff: diff --git a/ChangeLog b/ChangeLog
index 3f93455..701c097 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,6 @@
+2022-05-05: Fix: index entry label not being zero-terminated with corrupt input
 2022-05-03: Fix boundary checking error in markup search, that could cause buffer over-read with corrupt input
 2022-05-02: Fix typo in macro name
 2022-04-27: Fix undefined behavior when passing null to strdup
 2022-04-27: Fix wrong boundary checks in inflections parser resulting in stack buffer over-read with corrupt input
 2022-04-26: Fix text formatting
diff --git a/src/index.c b/src/index.c
index ca83675..109231c 100644
--- a/src/index.c
+++ b/src/index.c
@@ -27,15 +27,15 @@
 
 
 /**
  @brief Read index entry label from buffer pointing at index record data
  
- @param[in,out] output Output string
+ @param[in,out] output Output buffer (INDX_LABEL_SIZEMAX + 1 bytes)
  @param[in,out] buf MOBIBuffer structure, offset pointing at index entry label
  @param[in] length Number of bytes to be read
  @param[in] has_ligatures Decode ligatures if true
- @return Size of read label
+ @return Length of output string (without null terminator), on error buf->error set to MOBI_RET status
  */
 size_t mobi_indx_get_label(unsigned char *output, MOBIBuffer *buf, const size_t length, const size_t has_ligatures) {
     if (!output) {
         buf->error = MOBI_PARAM_ERR;
         return 0;
@@ -246,13 +246,13 @@ uint16_t mobi_ordt_lookup(const MOBIOrdt *ordt, const uint16_t offset) {
 /**
  @brief Get UTF-8 string from buffer, decoded by lookups in ORDT2 table
  
  @param[in] ordt MOBIOrdt structure (ORDT data and metadata)
  @param[in,out] buf MOBIBuffer structure with input string
- @param[in,out] output Output buffer (INDX_LABEL_SIZEMAX bytes)
+ @param[in,out] output Output buffer (INDX_LABEL_SIZEMAX + 1 bytes)
  @param[in] length Length of input string contained in buf
- @return Number of bytes read
+ @return Length of output string (without null terminator)
  */
 size_t mobi_getstring_ordt(const MOBIOrdt *ordt, MOBIBuffer *buf, unsigned char *output, size_t length) {
     size_t i = 0;
     size_t output_length = 0;
     const uint32_t bytemask = 0xbf;
@@ -360,16 +360,20 @@ static MOBI_RET mobi_parse_index_entry(MOBIIndx *indx, const MOBIIdxt idxt, cons
     size_t label_length = mobi_buffer_get8(buf);
     if (label_length > entry_length) {
         debug_print("Label length too long: %zu\n", label_length);
         return MOBI_DATA_CORRUPT;
     }
-    char text[INDX_LABEL_SIZEMAX];
+    char text[INDX_LABEL_SIZEMAX + 1];
     /* FIXME: what is ORDT1 for? */
     if (ordt->ordt2) {
         label_length = mobi_getstring_ordt(ordt, buf, (unsigned char*) text, label_length);
     } else {
         label_length = mobi_indx_get_label((unsigned char*) text, buf, label_length, indx->ligt_entries_count);
+        if (buf->error != MOBI_SUCCESS) {
+            debug_print("Buffer error reading label: %d\n", buf->error);
+            return MOBI_DATA_CORRUPT;
+        }
     }
     indx->entries[entry_number].label = malloc(label_length + 1);
     if (indx->entries[entry_number].label == NULL) {
         debug_print("Memory allocation failed (%zu bytes)\n", label_length);
         return MOBI_MALLOC_FAILED;

Vulnerable code:  @param[in,out] output Output string
 @return Size of read label
 @param[in,out] output Output buffer (INDX_LABEL_SIZEMAX bytes)
 @return Number of bytes read
    char text[INDX_LABEL_SIZEMAX];
Fixed code: 2022-05-05: Fix: index entry label not being zero-terminated with corrupt input
 @param[in,out] output Output buffer (INDX_LABEL_SIZEMAX + 1 bytes)
 @return Length of output string (without null terminator), on error buf->error set to MOBI_RET status
 @param[in,out] output Output buffer (INDX_LABEL_SIZEMAX + 1 bytes)
 @return Length of output string (without null terminator)
    char text[INDX_LABEL_SIZEMAX + 1];
        if (buf->error != MOBI_SUCCESS) {
            debug_print("Buffer error reading label: %d\n", buf->error);
            return MOBI_DATA_CORRUPT;
        }