From b0dbdbe4007f18777b3a54c9f79a5815d4543c9e Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 3 Dec 2023 19:55:03 +0100 Subject: [PATCH 1/2] ignore invalid DXF objects/entities --- CMakeLists.txt | 4 ++++ include/dwg.h | 1 + src/bits.h | 3 ++- src/config.h.in | 3 +++ src/encode.c | 45 ++++++++++++++++++++++++++++++++------------- src/in_dxf.c | 30 +++++++++++++++++++++++++----- src/out_dxf.c | 14 ++++++++------ src/out_dxfb.c | 10 ++++++---- src/out_json.c | 2 ++ 9 files changed, 83 insertions(+), 29 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cbf9341494..183e0ee4e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,8 @@ project(libredwg C) # Supported options -DLIBREDWG_LIBONLY=On # -DLIBREDWG_DISABLE_WRITE=On # -DLIBREDWG_DISABLE_JSON=On +# -DLIBREDWG_DISABLE_DXF=On +# -DLIBREDWG_DISABLE_IGNORE_INVALID_DXF=On # -DENABLE_MIMALLOC=On # -DENABLE_LTO=Off # -DDXF_PRECISION=6 @@ -35,6 +37,8 @@ option(ENABLE_LTO "IPO/LTO Link Time Optimizations (default ON)" ON) # FIXME enable # option(LIBREDWG_DISABLE_BINDINGS "no libredwg perl and python bindings" ON) option(LIBREDWG_DISABLE_JSON "no libredwg json support" OFF) +option(LIBREDWG_DISABLE_DXF "no libredwg dxf support" OFF) +option(LIBREDWG_DISABLE_IGNORE_INVALID_DXF "stop when invalid DXF object/entity encountered (don't skip)" OFF) # Rather disable installing a crippled shared lib. # Only use these for static in-tree libs. if (LIBREDWG_DISABLE_JSON) diff --git a/include/dwg.h b/include/dwg.h index d6db237420..a74a427153 100644 --- a/include/dwg.h +++ b/include/dwg.h @@ -8978,6 +8978,7 @@ typedef struct _dwg_object size_t hdlpos; /* relative offset, in bits */ BITCODE_B was_bitsize_set; /* internally for encode only */ BITCODE_B has_strings; /*!< r2007+ */ + BITCODE_B invalid; /*!< if object could not be completely processed */ BITCODE_RL stringstream_size; /*!< r2007+ in bits, unused */ BITCODE_UMC handlestream_size; /*!< r2010+ in bits */ size_t common_size; /* relative offset from type ... end common_entity_data */ diff --git a/src/bits.h b/src/bits.h index 694bff60ba..7c48b5f07a 100644 --- a/src/bits.h +++ b/src/bits.h @@ -77,11 +77,12 @@ typedef struct _bit_chain Dwg_Version_Type from_version; FILE *fh; BITCODE_RS codepage; + unsigned int dxf_line_number; } Bit_Chain; #define EMPTY_CHAIN(size) \ { \ - NULL, size, 0UL, 0, 0, R_INVALID, R_INVALID, NULL, 0 \ + NULL, size, 0UL, 0, 0, R_INVALID, R_INVALID, NULL, 0, -1 \ } // only if from r2007+ DWG, not JSON, DXF, add API diff --git a/src/config.h.in b/src/config.h.in index fe3ca5b163..3c7106aade 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -18,6 +18,9 @@ /* Define to disable DXF, JSON and other in/out modules. */ #undef DISABLE_DXF +/* Define to stop on invalid DXF, instead of skipping invalid objects/entities. */ +#undef DISABLE_IGNORE_INVALID_DXF + /* Define to disable JSON and GeoJSON in/out modules. */ #undef DISABLE_JSON diff --git a/src/encode.c b/src/encode.c index fb263791d7..cc1fa4f857 100644 --- a/src/encode.c +++ b/src/encode.c @@ -1174,6 +1174,7 @@ typedef struct BITCODE_RLL handle; size_t address; BITCODE_BL index; + BITCODE_B invalid; } Object_Map; /*-------------------------------------------------------------------------------- @@ -2525,14 +2526,14 @@ dwg_encode (Dwg_Data *restrict dwg, Bit_Chain *restrict dat) for (i = 0; i < dwg->num_objects; i++) { Dwg_Object *obj = &dwg->object[i]; - if (obj->fixedtype == DWG_TYPE_UNKNOWN_OBJ + if (!obj->invalid && (obj->fixedtype == DWG_TYPE_UNKNOWN_OBJ || obj->fixedtype == DWG_TYPE_UNKNOWN_ENT // WIPEOUT causes hang, TABLEGEOMETRY crash, MATERIAL causes ODA errors #ifndef DEBUG_CLASSES || (dwg->opts & DWG_OPTS_IN && (obj->fixedtype == DWG_TYPE_WIPEOUT || obj->fixedtype == DWG_TYPE_TABLEGEOMETRY - || obj->fixedtype == DWG_TYPE_MATERIAL)) + || obj->fixedtype == DWG_TYPE_MATERIAL))) #endif ) { @@ -2561,6 +2562,8 @@ dwg_encode (Dwg_Data *restrict dwg, Bit_Chain *restrict dat) for (i = 0; i < dwg->num_objects; i++) { Dwg_Object *obj = &dwg->object[i]; + if (obj->invalid) + continue; if (obj->fixedtype == DWG_TYPE_UNKNOWN_OBJ || obj->fixedtype == DWG_TYPE_UNKNOWN_ENT #ifndef DEBUG_CLASSES @@ -2596,6 +2599,8 @@ dwg_encode (Dwg_Data *restrict dwg, Bit_Chain *restrict dat) for (i = 0; i < dwg->num_objects; i++) { Dwg_Object *obj = &dwg->object[i]; + if (obj->invalid) + continue; if (obj->fixedtype == DWG_TYPE_MLEADERSTYLE) downconvert_MLEADERSTYLE (obj); else if (obj->fixedtype == DWG_TYPE_DIMSTYLE) @@ -3350,21 +3355,31 @@ dwg_encode (Dwg_Data *restrict dwg, Bit_Chain *restrict dat) { LOG_HANDLE ("\nSorting objects...\n"); for (i = 0; i < dwg->num_objects; i++) - fprintf (OUTPUT, "Object(%3i): " FORMAT_RLLx " / idx: %u\n", i, - dwg->object[i].handle.value, dwg->object[i].index); + if (!dwg->object[i].invalid) + fprintf (OUTPUT, "Object(%3i): " FORMAT_RLLx " / idx: %u\n", i, + dwg->object[i].handle.value, dwg->object[i].index); } // init unsorted for (i = 0; i < dwg->num_objects; i++) { Dwg_Object *obj = &dwg->object[i]; + if (obj->invalid) + { + omap[i].invalid = 1; + LOG_TRACE ("Skip invalid object %s " FORMAT_BL "\n", + obj->name ? obj->name : "", i) + continue; + } if (obj->type == DWG_TYPE_UNUSED) { + omap[i].invalid = 1; LOG_TRACE ("Skip unused object %s " FORMAT_BL " " FORMAT_RLLx "\n", obj->name ? obj->name : "", i, obj->handle.value) continue; } if (obj->type == DWG_TYPE_FREED) { + omap[i].invalid = 1; LOG_TRACE ("Skip freed object %s " FORMAT_BL " " FORMAT_RLLx "\n", obj->name ? obj->name : "", i, obj->handle.value) continue; @@ -3389,8 +3404,9 @@ dwg_encode (Dwg_Data *restrict dwg, Bit_Chain *restrict dat) { LOG_HANDLE ("\nSorted handles:\n"); for (i = 0; i < dwg->num_objects; i++) - fprintf (OUTPUT, "Handle(%3i): " FORMAT_RLLx " / idx: " FORMAT_BL "\n", - i, omap[i].handle, omap[i].index); + if (!omap[i].invalid) + fprintf (OUTPUT, "Handle(%3i): " FORMAT_RLLx " / idx: " FORMAT_BL "\n", + i, omap[i].handle, omap[i].index); } UNTIL (R_2000) @@ -3406,7 +3422,7 @@ dwg_encode (Dwg_Data *restrict dwg, Bit_Chain *restrict dat) BITCODE_UMC hdloff = omap[i].handle - (i ? omap[i - 1].handle : 0); BITCODE_MC off = (dat->byte - (i ? omap[i - 1].address : 0)) & INT32_MAX; size_t end_address; - if (!index && !omap[i].handle) + if (omap[i].invalid || (!index && !omap[i].handle)) continue; // skipped objects LOG_TRACE ("\n> Next object: " FORMAT_BL " Handleoff: " FORMAT_UMC " [UMC] Offset: " FORMAT_MC " [MC] @%" PRIuSIZE "\n" @@ -3463,9 +3479,10 @@ dwg_encode (Dwg_Data *restrict dwg, Bit_Chain *restrict dat) { LOG_HANDLE ("\nSorted objects:\n"); for (i = 0; i < dwg->num_objects; i++) - LOG_HANDLE ("Object(%d): " FORMAT_RLLx " / Address: %" PRIuSIZE - " / Idx: " FORMAT_BL "\n", - i, omap[i].handle, omap[i].address, omap[i].index); + if (!omap[i].invalid) + LOG_HANDLE ("Object(%d): " FORMAT_RLLx " / Address: %" PRIuSIZE + " / Idx: " FORMAT_BL "\n", + i, omap[i].handle, omap[i].address, omap[i].index); } bit_write_CRC (dat, pvzadr, 0xC0C1); @@ -3498,7 +3515,7 @@ dwg_encode (Dwg_Data *restrict dwg, Bit_Chain *restrict dat) BITCODE_MC offset; index = omap[i].index; - if (!index && !omap[i].handle) + if (omap[i].invalid || (!index && !omap[i].handle)) continue; // skipped objects handleoff = omap[i].handle - last_handle; bit_write_UMC (dat, handleoff); @@ -3654,7 +3671,7 @@ dwg_encode (Dwg_Data *restrict dwg, Bit_Chain *restrict dat) { \ unsigned char chain[8]; \ Bit_Chain hdat \ - = { chain, 8L, 0L, 0, 0, R_INVALID, R_INVALID, NULL, 30 }; \ + = { chain, 8L, 0L, 0, 0, R_INVALID, R_INVALID, NULL, 30, -1 }; \ bit_H_to_dat (&hdat, &dwg->header_vars.NAM->handleref); \ _obj->handles[i].name = #NAM; \ for (int k = 0; k < MIN ((int)_obj->handles[i].num_hdl, 8); k++) \ @@ -4238,7 +4255,7 @@ dwg_encode (Dwg_Data *restrict dwg, Bit_Chain *restrict dat) Dwg_R2004_Header *_obj = &dwg->r2004_header; Bit_Chain file_dat = { NULL, sizeof (Dwg_R2004_Header), 0UL, 0, 0, R_INVALID, R_INVALID, NULL, - 30 + 30, -1 }; Bit_Chain *orig_dat = dat; /* "AcFssFcAJMB" encrypted: 6840F8F7922AB5EF18DD0BF1 */ @@ -4672,6 +4689,8 @@ encode_preR13_entities (EntitySectionIndexR11 section, Bit_Chain *restrict dat, { Dwg_Object *obj = &dwg->object[index]; size_t size_pos = 0UL; + if (obj->invalid) + continue; // skip table objects or uninitialized entities if (obj->supertype != DWG_SUPERTYPE_ENTITY || !obj->tio.entity) { diff --git a/src/in_dxf.c b/src/in_dxf.c index 6584ebf464..696e15909c 100644 --- a/src/in_dxf.c +++ b/src/in_dxf.c @@ -225,6 +225,25 @@ static array_hdls *obj_hdls = NULL; pair = dxf_read_pair (dat); \ EXPECT_T_DXF (#field, dxf); \ } +#ifndef DISABLE_IGNORE_INVALID_DXF +# define HANDLE_INVALID(kind) \ + LOG_WARN ("DXF line %d: Failed to process %s in %s - SKIPPING", \ + dat->dxf_line_number, dxfname, #kind); \ + free (dxfname); \ + obj->invalid = 1; \ + for (;;) { \ + pair = dxf_read_pair (dat); \ + DXF_CHECK_EOF; \ + if (pair == NULL || pair->code == 0) { \ + break; \ + } \ + dxf_free_pair(pair); \ + } +#else +# define HANDLE_INVALID(kind) \ + obj->invalid = 1; \ + return DWG_ERR_INVALIDDWG; +#endif static void * xcalloc (size_t n, size_t s) @@ -586,7 +605,9 @@ dxf_read_pair (Bit_Chain *dat) return NULL; } if (is_binary) - LOG_HANDLE ("%4zx: ", dat->byte); + LOG_HANDLE ("%4zx: ", dat->byte) + else + dat->dxf_line_number += 2; pair->code = (short)dxf_read_rs (dat); if (dat->size - dat->byte < 4) // at least EOF\n goto err; @@ -12326,10 +12347,9 @@ dxf_entities_read (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) if (!pair) { Dwg_Object *obj = &dwg->object[idx]; - free (dxfname); if (idx != dwg->num_objects) obj->dxfname = NULL; - return DWG_ERR_INVALIDDWG; + HANDLE_INVALID(entities) } if (pair->code == 0 && pair->value.s) { @@ -12398,10 +12418,9 @@ dxf_objects_read (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) if (!pair) { Dwg_Object *obj = &dwg->object[idx]; - free (dxfname); if (idx != dwg->num_objects) obj->dxfname = NULL; - return DWG_ERR_INVALIDDWG; + HANDLE_INVALID(objects) } } else @@ -12772,6 +12791,7 @@ dwg_read_dxf (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) int error = 0; loglevel = dwg->opts & DWG_OPTS_LOGLEVEL; + dat->dxf_line_number = -1; if (!dat->chain && dat->fh) { error = dat_read_stream (dat, dat->fh); diff --git a/src/out_dxf.c b/src/out_dxf.c index 9a5522b25d..0cfadef1e7 100644 --- a/src/out_dxf.c +++ b/src/out_dxf.c @@ -3637,7 +3637,8 @@ dxf_entities_write (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) while (obj) { int i = obj->index; - error |= dwg_dxf_object (dat, obj, &i); + if (!obj->invalid) + error |= dwg_dxf_object (dat, obj, &i); obj = get_next_owned_block_entity (ms, obj); // until last_entity } // Then all pspace entities. just filter out other BLOCKS entities @@ -3647,7 +3648,8 @@ dxf_entities_write (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) while (obj) { int i = obj->index; - error |= dwg_dxf_object (dat, obj, &i); + if (!obj->invalid) + error |= dwg_dxf_object (dat, obj, &i); obj = get_next_owned_block_entity (ps, obj); } } @@ -3661,7 +3663,7 @@ dxf_entities_write (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) { int i = obj->index; Dwg_Object_Ref *owner = obj->tio.entity->ownerhandle; - if (!owner || (owner->obj == ms || owner->obj == ps)) + if (!obj->invalid && (!owner || (owner->obj == ms || owner->obj == ps))) error |= dwg_dxf_object (dat, obj, &i); obj = dwg_next_entity (obj); } @@ -3674,7 +3676,7 @@ dxf_entities_write (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) && obj->type != DWG_TYPE_ENDBLK) { Dwg_Object_Ref *owner = obj->tio.entity->ownerhandle; - if (!owner || (owner->obj == ms || owner->obj == ps)) + if (!obj->invalid && (!owner || (owner->obj == ms || owner->obj == ps))) error |= dwg_dxf_object (dat, obj, &i); } } @@ -3709,12 +3711,12 @@ dxf_objects_write (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) // The NOD (Named Object Dict) must be always the very first OBJECT, // not just DICTIONARY. nod = dwg_get_first_object (dwg, DWG_TYPE_DICTIONARY); - if (nod) + if (nod && !nod->invalid) error |= dwg_dxf_object (dat, nod, &i); for (i = 0; (BITCODE_BL)i < dwg->num_objects; i++) { const Dwg_Object *restrict obj = &dwg->object[i]; - if (obj == nod) + if (obj == nod || obj->invalid) continue; if (obj->supertype == DWG_SUPERTYPE_OBJECT && obj->type != DWG_TYPE_BLOCK_HEADER && !dwg_obj_is_control (obj)) diff --git a/src/out_dxfb.c b/src/out_dxfb.c index b7fbc7ee5a..acd830bba6 100644 --- a/src/out_dxfb.c +++ b/src/out_dxfb.c @@ -2404,7 +2404,8 @@ dxfb_entities_write (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) while (obj) { int i = obj->index; - error |= dwg_dxfb_object (dat, obj, &i); + if (!obj->invalid) + error |= dwg_dxfb_object (dat, obj, &i); obj = get_next_owned_block_entity (ms, obj); // until last_entity } // Then all pspace entities. just filter out other BLOCKS entities @@ -2414,7 +2415,8 @@ dxfb_entities_write (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) while (obj) { int i = obj->index; - error |= dwg_dxfb_object (dat, obj, &i); + if (!obj->invalid) + error |= dwg_dxfb_object (dat, obj, &i); obj = get_next_owned_block_entity (ps, obj); } } @@ -2433,12 +2435,12 @@ dxfb_objects_write (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) // The NOD (Named Object Dict) must be always the very first OBJECT, // not just DICTIONARY. nod = dwg_get_first_object (dwg, DWG_TYPE_DICTIONARY); - if (nod) + if (nod && !nod->invalid) error |= dwg_dxfb_object (dat, nod, &i); for (i = 0; (BITCODE_BL)i < dwg->num_objects; i++) { const Dwg_Object *restrict obj = &dwg->object[i]; - if (obj == nod) + if (obj == nod || obj->invalid) continue; if (obj->supertype == DWG_SUPERTYPE_OBJECT && obj->type != DWG_TYPE_BLOCK_HEADER && !dwg_obj_is_control (obj)) diff --git a/src/out_json.c b/src/out_json.c index 4b2c955057..4f963ecc61 100644 --- a/src/out_json.c +++ b/src/out_json.c @@ -2285,6 +2285,8 @@ json_objects_write (Bit_Chain *restrict dat, Dwg_Data *restrict dwg) { int error; Dwg_Object *obj = &dwg->object[i]; + if (obj->invalid) + continue; FIRSTPREFIX HASH; error = dwg_json_object (dat, obj); ENDHASH From 779ab07fc9e3fed1f4ed739a230c62fb3bac10b6 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 3 Dec 2023 20:00:04 +0100 Subject: [PATCH 2/2] ignore invalid DXF objects/entities --- src/in_dxf.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/in_dxf.c b/src/in_dxf.c index 696e15909c..ae024601b0 100644 --- a/src/in_dxf.c +++ b/src/in_dxf.c @@ -241,6 +241,9 @@ static array_hdls *obj_hdls = NULL; } #else # define HANDLE_INVALID(kind) \ + LOG_WARN ("DXF line %d: Failed to process %s in %s", \ + dat->dxf_line_number, dxfname, #kind); \ + free (dxfname); \ obj->invalid = 1; \ return DWG_ERR_INVALIDDWG; #endif