First implementation of GDS2 coordinate overflow checks.

This commit is contained in:
Matthias Koefferlein 2024-07-29 19:54:29 +02:00
parent d3921844d6
commit b7e2b59852
2 changed files with 320 additions and 99 deletions

View File

@ -51,27 +51,110 @@ GDS2WriterBase::GDS2WriterBase ()
// .. nothing yet ..
}
static int safe_scale (double sf, int value)
static int32_t safe_convert_to_int32 (int32_t value)
{
double i = floor (sf * value + 0.5);
if (i < double (std::numeric_limits<int>::min ())) {
throw tl::Exception ("Scaling failed: coordinate underflow");
}
if (i > double (std::numeric_limits<int>::max ())) {
throw tl::Exception ("Scaling failed: coordinate overflow");
}
return int (i);
return value;
}
inline int scale (double sf, int value)
static int32_t safe_convert_to_int32 (uint32_t value)
{
if (value > std::numeric_limits<int32_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("Coordinate overflow")));
}
return int32_t (value);
}
static int32_t safe_convert_to_int32 (int64_t value)
{
if (value < std::numeric_limits<int32_t>::min ()) {
throw tl::Exception (tl::to_string (tr ("Coordinate underflow")));
}
if (value > std::numeric_limits<int32_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("Coordinate overflow")));
}
return int32_t (value);
}
static int32_t safe_convert_to_int32 (uint64_t value)
{
if (value > std::numeric_limits<int32_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("Coordinate overflow")));
}
return int32_t (value);
}
template <class T>
static int32_t safe_scale (double sf, T value)
{
double i = floor (sf * value + 0.5);
if (i < double (std::numeric_limits<int32_t>::min ())) {
throw tl::Exception (tl::to_string (tr ("Scaling failed: coordinate underflow")));
}
if (i > double (std::numeric_limits<int32_t>::max ())) {
throw tl::Exception (tl::to_string (tr ("Scaling failed: coordinate overflow")));
}
return int32_t (i);
}
template <class T>
inline int32_t scale (double sf, T value)
{
if (sf == 1.0) {
return value;
return safe_convert_to_int32 (value);
} else {
return safe_scale (sf, value);
}
}
static uint16_t safe_convert_to_uint16 (int16_t value)
{
// we accept this as GDS is not well defined here ...
return value;
}
static uint16_t safe_convert_to_uint16 (uint16_t value)
{
return value;
}
static uint16_t safe_convert_to_uint16 (int32_t value)
{
if (value < 0) {
throw tl::Exception (tl::to_string (tr ("Short unsigned integer underflow")));
}
if (value > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("Short unsigned integer overflow")));
}
return uint16_t (value);
}
static uint16_t safe_convert_to_uint16 (uint32_t value)
{
if (value > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("Short unsigned integer overflow")));
}
return uint16_t (value);
}
static uint16_t safe_convert_to_uint16 (int64_t value)
{
if (value < 0) {
throw tl::Exception (tl::to_string (tr ("Short unsigned integer underflow")));
}
if (value > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("Short unsigned integer overflow")));
}
return uint16_t (value);
}
static uint16_t safe_convert_to_uint16 (uint64_t value)
{
if (value > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("Short unsigned integer overflow")));
}
return uint16_t (value);
}
void
GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data, const std::vector<db::cell_index_type> &cells)
{
@ -117,7 +200,7 @@ GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data,
write_record_size (6);
write_record (sPROPATTR);
write_short (short (std::distance (std::vector <std::string>::const_iterator (context_prop_strings.begin ()), s))); // = user string
write_short (safe_convert_to_uint16 (std::distance (std::vector <std::string>::const_iterator (context_prop_strings.begin ()), s))); // = user string
write_string_record (sPROPVALUE, *s);
@ -156,7 +239,7 @@ GDS2WriterBase::write_context_cell (db::Layout &layout, const short *time_data,
write_record_size (6);
write_record (sPROPATTR);
write_short (short (std::distance (std::vector <std::string>::const_iterator (context_prop_strings.begin ()), s))); // = user string
write_short (safe_convert_to_uint16 (std::distance (std::vector <std::string>::const_iterator (context_prop_strings.begin ()), s))); // = user string
write_string_record (sPROPVALUE, *s);
@ -266,7 +349,11 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
write_time (time_data);
write_time (time_data);
write_string_record (sLIBNAME, gds2_options.libname);
try {
write_string_record (sLIBNAME, gds2_options.libname);
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (", writing LIBNAME")));
}
write_record_size (4 + 8 * 2);
write_record (sUNITS);
@ -276,7 +363,11 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
// layout properties
if (gds2_options.write_file_properties && layout.prop_id () != 0) {
write_properties (layout, layout.prop_id ());
try {
write_properties (layout, layout.prop_id ());
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (", writing layout properties")));
}
}
// write context info
@ -291,7 +382,11 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
}
if (has_context) {
write_context_cell (layout, time_data, cells);
try {
write_context_cell (layout, time_data, cells);
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (", writing context cell")));
}
}
// body
@ -302,88 +397,118 @@ GDS2WriterBase::write (db::Layout &layout, tl::OutputStream &stream, const db::S
const db::Cell &cref (layout.cell (*cell));
// don't write ghost cells unless they are not empty (any more)
// also don't write proxy cells which are not employed
if ((! cref.is_ghost_cell () || ! cref.empty ()) && (! cref.is_proxy () || ! cref.is_top ())) {
try {
// cell header
// don't write ghost cells unless they are not empty (any more)
// also don't write proxy cells which are not employed
if ((! cref.is_ghost_cell () || ! cref.empty ()) && (! cref.is_proxy () || ! cref.is_top ())) {
write_record_size (4 + 12 * 2);
write_record (sBGNSTR);
write_time (time_data);
write_time (time_data);
// cell header
write_string_record (sSTRNAME, m_cell_name_map.cell_name (*cell));
// cell body
if (gds2_options.write_cell_properties && cref.prop_id () != 0) {
write_properties (layout, cref.prop_id ());
}
// instances
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
// write only instances to selected cells
if (options.keep_instances () || cell_set.find (inst->cell_index ()) != cell_set.end ()) {
progress_checkpoint ();
write_inst (sf, *inst, true /*normalize*/, resolve_skew_arrays, layout, inst->prop_id ());
write_record_size (4 + 12 * 2);
write_record (sBGNSTR);
write_time (time_data);
write_time (time_data);
try {
write_string_record (sSTRNAME, m_cell_name_map.cell_name (*cell));
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (", writing cell name")));
}
}
// cell body
// shapes
if (gds2_options.write_cell_properties && cref.prop_id () != 0) {
try {
write_properties (layout, cref.prop_id ());
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (", writing layout properties")));
}
}
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
if (layout.is_valid_layer (l->first)) {
// instances
int layer = l->second.layer;
int datatype = l->second.datatype;
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::EdgePairs | db::ShapeIterator::Paths | db::ShapeIterator::Texts));
while (! shape.at_end ()) {
// write only instances to selected cells
if (options.keep_instances () || cell_set.find (inst->cell_index ()) != cell_set.end ()) {
progress_checkpoint ();
if (shape->is_text ()) {
write_text (layer, datatype, sf, dbu, *shape, layout, shape->prop_id ());
} else if (shape->is_polygon ()) {
write_polygon (layer, datatype, sf, *shape, multi_xy, max_vertex_count, layout, shape->prop_id ());
} else if (shape->is_edge ()) {
write_edge (layer, datatype, sf, *shape, layout, shape->prop_id ());
} else if (shape->is_edge_pair ()) {
write_edge (layer, datatype, sf, shape->edge_pair ().first (), layout, shape->prop_id ());
write_edge (layer, datatype, sf, shape->edge_pair ().second (), layout, shape->prop_id ());
} else if (shape->is_path ()) {
if (no_zero_length_paths && (shape->path_length () - shape->path_extensions ().first - shape->path_extensions ().second) == 0) {
// eliminate the zero-width path
db::Polygon poly;
shape->polygon (poly);
write_polygon (layer, datatype, sf, poly, multi_xy, max_vertex_count, layout, shape->prop_id (), false);
} else {
write_path (layer, datatype, sf, *shape, multi_xy, layout, shape->prop_id ());
}
} else if (shape->is_box ()) {
write_box (layer, datatype, sf, *shape, layout, shape->prop_id ());
try {
write_inst (sf, *inst, true /*normalize*/, resolve_skew_arrays, layout, inst->prop_id ());
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::to_string (tr (", writing instances")));
}
++shape;
}
}
// shapes
for (std::vector <std::pair <unsigned int, db::LayerProperties> >::const_iterator l = layers.begin (); l != layers.end (); ++l) {
if (layout.is_valid_layer (l->first) && l->second.layer >= 0 && l->second.datatype >= 0) {
int layer = l->second.layer;
if (layer > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write layer numbers larger than %d to GDS2 streams")), int (std::numeric_limits<uint16_t>::max ())));
}
int datatype = l->second.datatype;
if (datatype > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write datatype numbers larger than %d to GDS2 streams")), int (std::numeric_limits<uint16_t>::max ())));
}
try {
db::ShapeIterator shape (cref.shapes (l->first).begin (db::ShapeIterator::Boxes | db::ShapeIterator::Polygons | db::ShapeIterator::Edges | db::ShapeIterator::EdgePairs | db::ShapeIterator::Paths | db::ShapeIterator::Texts));
while (! shape.at_end ()) {
progress_checkpoint ();
if (shape->is_text ()) {
write_text (layer, datatype, sf, dbu, *shape, layout, shape->prop_id ());
} else if (shape->is_polygon ()) {
write_polygon (layer, datatype, sf, *shape, multi_xy, max_vertex_count, layout, shape->prop_id ());
} else if (shape->is_edge ()) {
write_edge (layer, datatype, sf, *shape, layout, shape->prop_id ());
} else if (shape->is_edge_pair ()) {
write_edge (layer, datatype, sf, shape->edge_pair ().first (), layout, shape->prop_id ());
write_edge (layer, datatype, sf, shape->edge_pair ().second (), layout, shape->prop_id ());
} else if (shape->is_path ()) {
if (no_zero_length_paths && (shape->path_length () - shape->path_extensions ().first - shape->path_extensions ().second) == 0) {
// eliminate the zero-width path
db::Polygon poly;
shape->polygon (poly);
write_polygon (layer, datatype, sf, poly, multi_xy, max_vertex_count, layout, shape->prop_id (), false);
} else {
write_path (layer, datatype, sf, *shape, multi_xy, layout, shape->prop_id ());
}
} else if (shape->is_box ()) {
write_box (layer, datatype, sf, *shape, layout, shape->prop_id ());
}
++shape;
}
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (", writing layer %d/%d")), layer, datatype));
}
}
}
// end of cell
write_record_size (4);
write_record (sENDSTR);
}
// end of cell
write_record_size (4);
write_record (sENDSTR);
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (", writing cell '%s'")), layout.cell_name (*cell)));
}
}
@ -509,8 +634,8 @@ GDS2WriterBase::write_inst (double sf, const db::Instance &instance, bool normal
if (is_reg) {
write_record_size (4 + 2 * 2);
write_record (sCOLROW);
if (amax > 32767 || bmax > 32767) {
throw tl::Exception (tl::to_string (tr ("Cannot write array references with more than 32767 columns or rows to GDS2 streams")));
if (amax > std::numeric_limits<int16_t>::max () || bmax > std::numeric_limits<int16_t>::max ()) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot write array references with more than %d columns or rows to GDS2 streams")), int (std::numeric_limits<int16_t>::max ())));
}
write_short (std::max ((unsigned long) 1, bmax));
write_short (std::max ((unsigned long) 1, amax));
@ -522,10 +647,10 @@ GDS2WriterBase::write_inst (double sf, const db::Instance &instance, bool normal
write_int (scale (sf, t.disp ().y ()));
if (is_reg) {
write_int (scale (sf, t.disp ().x () + b.x () * bmax));
write_int (scale (sf, t.disp ().y () + b.y () * bmax));
write_int (scale (sf, t.disp ().x () + a.x () * amax));
write_int (scale (sf, t.disp ().y () + a.y () * amax));
write_int (scale (sf, t.disp ().x () + b.x () * (long) bmax));
write_int (scale (sf, t.disp ().y () + b.y () * (long) bmax));
write_int (scale (sf, t.disp ().x () + a.x () * (long) amax));
write_int (scale (sf, t.disp ().y () + a.y () * (long) amax));
}
finish (layout, prop_id);
@ -548,11 +673,11 @@ GDS2WriterBase::write_box (int layer, int datatype, double sf, const db::Shape &
write_record_size (4 + 2);
write_record (sLAYER);
write_short (layer);
write_short (safe_convert_to_uint16 (layer));
write_record_size (4 + 2);
write_record (sDATATYPE);
write_short (datatype);
write_short (safe_convert_to_uint16 (datatype));
write_record_size (4 + 5 * 2 * 4);
write_record (sXY);
@ -582,11 +707,11 @@ GDS2WriterBase::write_path (int layer, int datatype, double sf, const db::Shape
write_record_size (4 + 2);
write_record (sLAYER);
write_short (layer);
write_short (safe_convert_to_uint16 (layer));
write_record_size (4 + 2);
write_record (sDATATYPE);
write_short (datatype);
write_short (safe_convert_to_uint16 (datatype));
short type = 0;
db::Coord w = path.width ();
@ -662,11 +787,11 @@ GDS2WriterBase::write_edge (int layer, int datatype, double sf, const db::Edge &
write_record_size (4 + 2);
write_record (sLAYER);
write_short (layer);
write_short (safe_convert_to_uint16 (layer));
write_record_size (4 + 2);
write_record (sDATATYPE);
write_short (datatype);
write_short (safe_convert_to_uint16 (datatype));
write_record_size (4 + 2);
write_record (sPATHTYPE);
@ -696,11 +821,11 @@ GDS2WriterBase::write_text (int layer, int datatype, double sf, double dbu, cons
write_record_size (4 + 2);
write_record (sLAYER);
write_short (layer);
write_short (safe_convert_to_uint16 (layer));
write_record_size (4 + 2);
write_record (sTEXTTYPE);
write_short (datatype);
write_short (safe_convert_to_uint16 (datatype));
if (shape.text_halign () != db::NoHAlign || shape.text_valign () != db::NoVAlign || shape.text_font () != db::NoFont) {
short ha = short (shape.text_halign () == db::NoHAlign ? db::HAlignLeft : shape.text_halign ());
@ -781,11 +906,11 @@ GDS2WriterBase::write_polygon (int layer, int datatype, double sf, const db::Pol
write_record_size (4 + 2);
write_record (sLAYER);
write_short (layer);
write_short (safe_convert_to_uint16 (layer));
write_record_size (4 + 2);
write_record (sDATATYPE);
write_short (datatype);
write_short (safe_convert_to_uint16 (datatype));
size_t n = polygon.vertices ();
@ -856,11 +981,11 @@ GDS2WriterBase::write_polygon (int layer, int datatype, double sf, const db::Sha
write_record_size (4 + 2);
write_record (sLAYER);
write_short (layer);
write_short (safe_convert_to_uint16 (layer));
write_record_size (4 + 2);
write_record (sDATATYPE);
write_short (datatype);
write_short (safe_convert_to_uint16 (datatype));
db::Shape::point_iterator e (shape.begin_hull ());
while (n > 0) {
@ -911,11 +1036,11 @@ GDS2WriterBase::write_properties (const db::Layout &layout, db::properties_id_ty
attr = name.to_long ();
}
if (attr >= 0 && attr < 65535) {
if (attr >= 0 && attr <= std::numeric_limits<uint16_t>::max ()) {
write_record_size (6);
write_record (sPROPATTR);
write_short (attr);
write_short ((int16_t) attr);
write_string_record (sPROPVALUE, p->second.to_string ());
@ -938,7 +1063,11 @@ GDS2WriterBase::finish (const db::Layout &layout, db::properties_id_type prop_id
void
GDS2WriterBase::write_string_record (short record, const std::string &t)
{
write_record_size (4 + (int16_t (t.size () + 1) / 2) * 2);
size_t rs = 4 + ((t.size () + 1) / 2) * 2;
if (rs > std::numeric_limits<uint16_t>::max ()) {
throw tl::Exception (tl::to_string (tr ("String max. length overflow")));
}
write_record_size (uint16_t (rs));
write_record (record);
write_string (t);
}

View File

@ -1312,6 +1312,96 @@ TEST(130a)
EXPECT_EQ (layout_read.meta_info ("b").value.to_string (), "nil");
}
// Limits
static std::string run_test_with_error (double sf, db::Layout &layout)
{
try {
tl::OutputMemoryStream buffer;
tl::OutputStream stream (buffer);
db::SaveLayoutOptions options;
options.set_format ("GDS2");
options.set_scale_factor (sf);
db::Writer writer (options);
writer.write (layout, stream);
return std::string ();
} catch (tl::Exception &ex) {
return ex.msg ();
}
}
static std::string huge_string ()
{
std::string n;
for (unsigned int i = 0; i < 100000; ++i) {
n += "A";
}
return n;
}
// Exceeding limits
TEST(140)
{
db::Layout layout;
db::cell_index_type top_index = layout.add_cell ("TOP");
db::Cell &top = layout.cell (top_index);
unsigned int l1 = layout.insert_layer (db::LayerProperties (1, 0));
top.shapes (l1).insert (db::Text (huge_string (), db::Trans ()));
EXPECT_EQ (run_test_with_error (1.0, layout), "String max. length overflow, writing layer 1/0, writing cell 'TOP'");
}
TEST(141)
{
db::Layout layout;
db::cell_index_type top_index = layout.add_cell ("TOP");
db::Cell &top = layout.cell (top_index);
unsigned int l1 = layout.insert_layer (db::LayerProperties (100000, 0));
top.shapes (l1).insert (db::Box (0, 0, 100, 200));
EXPECT_EQ (run_test_with_error (1.0, layout), "Cannot write layer numbers larger than 65535 to GDS2 streams, writing cell 'TOP'");
}
TEST(142)
{
db::Layout layout;
db::cell_index_type top_index = layout.add_cell ("TOP");
db::Cell &top = layout.cell (top_index);
db::cell_index_type child_index = layout.add_cell ("CHILD");
db::Cell &child = layout.cell (child_index);
unsigned int l1 = layout.insert_layer (db::LayerProperties (1, 0));
child.shapes (l1).insert (db::Box (0, 0, 100, 200));
top.insert (db::CellInstArray (child_index, db::Trans (), db::Vector (100000000, 0), db::Vector (0, 100000000), 10, 10));
EXPECT_EQ (run_test_with_error (1.0, layout), ""); // no error
top.clear_insts ();
top.insert (db::CellInstArray (child_index, db::Trans (), db::Vector (100000000, 0), db::Vector (0, 100000000), 100, 100));
EXPECT_EQ (run_test_with_error (1.0, layout), "Coordinate overflow, writing instances, writing cell 'TOP'");
top.clear_insts ();
top.insert (db::CellInstArray (child_index, db::Trans (), db::Vector (100, 0), db::Vector (0, 100), 100000, 100));
EXPECT_EQ (run_test_with_error (1.0, layout), "Cannot write array references with more than 32767 columns or rows to GDS2 streams, writing instances, writing cell 'TOP'");
}
TEST(143)
{
db::Layout layout;
db::cell_index_type top_index = layout.add_cell ("TOP");
db::Cell &top = layout.cell (top_index);
unsigned int l1 = layout.insert_layer (db::LayerProperties (1, 0));
top.shapes (l1).insert (db::Box (-2000000000, 0, 0, 200000000));
EXPECT_EQ (run_test_with_error (1.0, layout), "");
EXPECT_EQ (run_test_with_error (23.0, layout), "Scaling failed: coordinate underflow, writing layer 1/0, writing cell 'TOP'");
}
// Extreme fracturing by max. points
TEST(166)
{
@ -1320,3 +1410,5 @@ TEST(166)
run_test (_this, "t166.oas.gz", "t166_au.gds.gz", false, opt);
}