CVE-2022-2279: NULL Pointer Dereference (CWE-476)
value: NULL Pointer Dereference in GitHub repository bfabiszewski/libmobi prior to 0.11.
CWE description: The product dereferences a pointer that it expects to be valid but is NULL.
CVSS v3 base score: 5.5
Published: 2022-07-01T09:15Z
Repository: https://github.com/bfabiszewski/libmobi
Language: C
Files changed: ChangeLog, src/opf.c, src/util.c, tools/common.c
Commit date: 2022-04-27 12:35:39 +0200
Commit message: Fix undefined behavior when passing null to strdup

Diff: diff --git a/ChangeLog b/ChangeLog
index b431347..8f48750 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,6 @@
+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
 2022-04-26: Fix array boundary check when parsing inflections which could result in buffer over-read with corrupt input
 2022-04-23: Fix formatting
 2022-04-23: Fix checking boundary of deobfuscation key which could cause buffer over-read with corrupt data
diff --git a/src/opf.c b/src/opf.c
index eb36750..02e984b 100644
--- a/src/opf.c
+++ b/src/opf.c
@@ -1150,28 +1150,34 @@ MOBI_RET mobi_build_opf_metadata(OPF *opf,  const MOBIData *m, const MOBIRawml *
     }
     /* write optional elements */
     if (mobi_is_dictionary(m)) {
         if (opf->metadata->x_meta->dictionary_in_lang == NULL) {
             if (m->mh && m->mh->dict_input_lang) {
-                opf->metadata->x_meta->dictionary_in_lang = calloc(OPF_META_MAX_TAGS, sizeof(char*));
-                if (opf->metadata->x_meta->dictionary_in_lang == NULL) {
-                    debug_print("%s\n", "Memory allocation failed");
-                    return MOBI_MALLOC_FAILED;
-                }
                 uint32_t dict_lang_in = *m->mh->dict_input_lang;
-                opf->metadata->x_meta->dictionary_in_lang[0] = strdup(mobi_get_locale_string(dict_lang_in));
+                const char *lang = mobi_get_locale_string(dict_lang_in);
+                if (lang) {
+                    opf->metadata->x_meta->dictionary_in_lang = calloc(OPF_META_MAX_TAGS, sizeof(char*));
+                    if (opf->metadata->x_meta->dictionary_in_lang == NULL) {
+                        debug_print("%s\n", "Memory allocation failed");
+                        return MOBI_MALLOC_FAILED;
+                    }
+                    opf->metadata->x_meta->dictionary_in_lang[0] = strdup(lang);
+                }
             }
         }
         if (opf->metadata->x_meta->dictionary_out_lang == NULL) {
             if (m->mh && m->mh->dict_output_lang) {
-                opf->metadata->x_meta->dictionary_out_lang = calloc(OPF_META_MAX_TAGS, sizeof(char*));
-                if (opf->metadata->x_meta->dictionary_out_lang == NULL) {
-                    debug_print("%s\n", "Memory allocation failed");
-                    return MOBI_MALLOC_FAILED;
+                uint32_t dict_lang_out = *m->mh->dict_output_lang;
+                const char *lang = mobi_get_locale_string(dict_lang_out);
+                if (lang) {
+                    opf->metadata->x_meta->dictionary_out_lang = calloc(OPF_META_MAX_TAGS, sizeof(char*));
+                    if (opf->metadata->x_meta->dictionary_out_lang == NULL) {
+                        debug_print("%s\n", "Memory allocation failed");
+                        return MOBI_MALLOC_FAILED;
+                    }
+                    opf->metadata->x_meta->dictionary_out_lang[0] = strdup(lang);
                 }
-                uint32_t dict_lang_in = *m->mh->dict_output_lang;
-                opf->metadata->x_meta->dictionary_out_lang[0] = strdup(mobi_get_locale_string(dict_lang_in));
             }
         }
         if (rawml->orth->orth_index_name) {
             opf->metadata->x_meta->default_lookup_index = calloc(OPF_META_MAX_TAGS, sizeof(char*));
             if (opf->metadata->x_meta->default_lookup_index == NULL) {
diff --git a/src/util.c b/src/util.c
index d0d57dd..a9260d4 100644
--- a/src/util.c
+++ b/src/util.c
@@ -639,11 +639,11 @@ static const char *mobi_locale[MOBI_LANG_MAX][MOBI_REGION_MAX] = {
  
  Locale strings are based on IANA language-subtag registry with some custom Mobipocket modifications.
  See mobi_locale array.
  
  @param[in] locale_number Mobipocket locale number (as stored in MOBI header)
- @return Pointer to locale string in mobi_locale array
+ @return Pointer to locale string in mobi_locale array or NULL on error
  */
 const char * mobi_get_locale_string(const uint32_t locale_number) {
     uint8_t lang_code = locale_number & 0xffU;
     uint32_t region_code = (locale_number >> 8) / 4;
     if (lang_code >= MOBI_LANG_MAX || region_code >= MOBI_REGION_MAX) {
diff --git a/tools/common.c b/tools/common.c
index 6c7cbfe..d26f37c 100644
--- a/tools/common.c
+++ b/tools/common.c
@@ -328,11 +328,11 @@ void print_summary(const MOBIData *m) {
         printf("Dictionary");
         if (m->mh && m->mh->dict_input_lang && m->mh->dict_output_lang &&
             *m->mh->dict_input_lang && *m->mh->dict_output_lang) {
             const char *locale_in = mobi_get_locale_string(*m->mh->dict_input_lang);
             const char *locale_out = mobi_get_locale_string(*m->mh->dict_output_lang);
-            printf(": %s => %s", locale_in, locale_out);
+            printf(": %s => %s", locale_in ? locale_in : "unknown", locale_out ? locale_out : "unknown");
         }
         printf("\n");
     }
     printf("__\n");
     if (strcmp(m->ph->type, "TEXt") == 0) {

Vulnerable code:                 opf->metadata->x_meta->dictionary_in_lang = calloc(OPF_META_MAX_TAGS, sizeof(char*));
                if (opf->metadata->x_meta->dictionary_in_lang == NULL) {
                    debug_print("%s\n", "Memory allocation failed");
                    return MOBI_MALLOC_FAILED;
                }
                opf->metadata->x_meta->dictionary_in_lang[0] = strdup(mobi_get_locale_string(dict_lang_in));
                opf->metadata->x_meta->dictionary_out_lang = calloc(OPF_META_MAX_TAGS, sizeof(char*));
                if (opf->metadata->x_meta->dictionary_out_lang == NULL) {
                    debug_print("%s\n", "Memory allocation failed");
                    return MOBI_MALLOC_FAILED;
                uint32_t dict_lang_in = *m->mh->dict_output_lang;
                opf->metadata->x_meta->dictionary_out_lang[0] = strdup(mobi_get_locale_string(dict_lang_in));
 @return Pointer to locale string in mobi_locale array
            printf(": %s => %s", locale_in, locale_out);
Fixed code: 2022-04-27: Fix undefined behavior when passing null to strdup
                const char *lang = mobi_get_locale_string(dict_lang_in);
                if (lang) {
                    opf->metadata->x_meta->dictionary_in_lang = calloc(OPF_META_MAX_TAGS, sizeof(char*));
                    if (opf->metadata->x_meta->dictionary_in_lang == NULL) {
                        debug_print("%s\n", "Memory allocation failed");
                        return MOBI_MALLOC_FAILED;
                    }
                    opf->metadata->x_meta->dictionary_in_lang[0] = strdup(lang);
                }
                uint32_t dict_lang_out = *m->mh->dict_output_lang;
                const char *lang = mobi_get_locale_string(dict_lang_out);
                if (lang) {
                    opf->metadata->x_meta->dictionary_out_lang = calloc(OPF_META_MAX_TAGS, sizeof(char*));
                    if (opf->metadata->x_meta->dictionary_out_lang == NULL) {
                        debug_print("%s\n", "Memory allocation failed");
                        return MOBI_MALLOC_FAILED;
                    }
                    opf->metadata->x_meta->dictionary_out_lang[0] = strdup(lang);
 @return Pointer to locale string in mobi_locale array or NULL on error
            printf(": %s => %s", locale_in ? locale_in : "unknown", locale_out ? locale_out : "unknown");