Skip to content

Datamatrix barcode, EDIFACT (EDF) mode: lookAheadTest() can recommend EDF even though the next character is outside the EDF range (32–94) — causes an infinite loop (ASCII↔EDF oscillation) #878

Description

@devmikon

TCPDF2DBarcode (or the Datamatrix class in include/barcodes/datamatrix.php directly) can get stuck in an infinite loop for certain inputs while generating a Data Matrix barcode, without raising any error or timeout message — the PHP process just hangs until an external timeout (e.g. max_execution_time) kicks in.

Root cause:

lookAheadTest() (include/barcodes/datamatrix.php:524-645, Annex P STEP K, specifically line 538 onward) picks the encoding mode for the remaining tail purely from accumulated, weighted cost estimates. It does not check whether the very next character ($data[$pos]) actually belongs to the EDIFACT character set (ASCII 32–94, see isCharMode(), lines 477-514, in particular the ENC_EDF branch at line 496). For a short remaining tail, the cost calculation can still come out in favor of EDF even though the next character (e.g. a lowercase letter or a symbol outside 32–94) can't be encoded there at all.

The EDF field builder (case ENC_EDF: in getHighLevelEncoding(), starting at line 873) can't consume such a character: $epos is only incremented when isCharMode($chr, ENC_EDF) holds (line 882). If the very first character is already invalid, $epos stays equal to $pos; the code unlatches back to ASCII (line 887 onward) but sets $pos = $epos (line 922) — i.e. unchanged. The outer loop while ($pos < $data_length) in getHighLevelEncoding() (line 715) then calls lookAheadTest() again at the exact same position (call sites: line 727 in the ENC_ASCII branch, line 808 in the shared ENC_C40/ENC_TXT/ENC_X12 branch, line 936 in the ENC_BASE256 branch) — with the identical result. This produces an infinite loop oscillating between ASCII and EDF mode, with $pos never advancing.

Unlike the actively maintained successor library tecnickcom/tc-lib-barcode (which has an indirect capacity check via getPaddingSize() that eventually breaks the oscillation with an exception after a bounded number of rounds), there is no upper bound here at all — the loop does not terminate on its own.

Steps to reproduce:

<?php
require_once(dirname(__FILE__) . '/tcpdf_barcodes_2d_include.php');

$example = '<MP U="XXX" l="de-DE" v="028"><P g="Konias" f="Michael" egk="A123456789"/><A n="Dr. med. NAME" lanr="010101010" t="2026-09-22T13:32:36"/><M p="12345678" du="1" t="2" i="Alle 2 Stunden; bis zu alle 6 - 8 h, max. 8 x / d, Mindesabstand 1 h" x="bei Schmerzen NRS 4 - 10"/></S></MP>';

ini_set('max_execution_time', 10);

$barcodeobj = new TCPDF2DBarcode($example, 'DATAMATRIX');

file_put_contents('test.png', $barcodeobj->getBarcodePngData(5, 5, array(0, 0, 0)));

Expected behavior:

A validly encoded barcode.

Possible fix:

Before accepting an ENC_EDF recommendation from lookAheadTest(), verify that $data[$pos] actually belongs to the EDF character set (isCharMode(ord($data[$pos]), ENC_EDF)), and fall back to the current mode otherwise (no mode-switch codeword written). This can be added at the three call sites listed above (lines 727, 808, 936). A fix placed directly inside the case ENC_EDF: handler itself would come too late, since a mode-switch codeword has already been written to the output stream by the time the invalid character is detected there.

I'm aware TCPDF is marked as deprecated, but I'm still reporting this since the file continues to be shipped and used in production.

Happy to prepare a patch with the fix described above if useful.

Related issue in the successor library: tecnickcom/tc-lib-barcode#93

Environment:

  • tecnickcom/tcpdf: release 6.11.4 (2026-08-28)
  • VERSION: 6.11.4
  • File version (datamatrix.php): 1.0.008
  • PHP: 8.3.33 (Windows, CLI)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions