zorantica / plsql-qr-code

Oracle PL/SQL Package and APEX plugin for QR Code Generation
MIT License
52 stars 20 forks source link

crash on some strings #4

Closed Mutantpenguin closed 3 years ago

Mutantpenguin commented 3 years ago

The package crashes on strings which have a special length. My environment is an Oracle Server 12.2.0.1.0 with characterset set to AL32UTF8. I first thought there might be some problem with my input, but just using a string only containing the character "A" multiple times triggers the same error:

ORA-06502: PL/SQL: numeric or value error: character string buffer too small

in the function f_encode_data in this block of code:

       --final filling of data with 11101100 and 00010001 alternatively until full length is reached
       LOOP
          EXIT WHEN length(lcData) = lnReqNumOfBits;
          lcData := lcData || '11101100';
          p_debug('Additional padding with: 11101100', 2);
          EXIT WHEN length(lcData) = lnReqNumOfBits;
          lcData := lcData || '00010001';
          p_debug('Additional padding with: 00010001', 2);
      END LOOP;

Example:

select zt_qr.f_qr_as_bmp( 'AAAAAAAAAAAAAAAA', 'Q', 'N' ) qr_code
from dual;

Changing just one uppercase "A" against a lowercase one make the error go away.

I wrote a small script to find problematic strings:

DECLARE
  /*
  Error correction modes:
  L - Low       Recovers 7% of data
  M - Medium    Recovers 15% of data
  Q - Quartile  Recovers 25% of data
  H - High      Recovers 30% of data
  */

  lc_quality CONSTANT VARCHAR2( 1 ) := 'H';
BEGIN
  FOR i IN 1 .. 500
    LOOP
      DECLARE
        l_the_string VARCHAR2( 2000 );
      BEGIN
        FOR j IN 1 .. i
          LOOP
            l_the_string := l_the_string || 'A';
        END LOOP;

        DECLARE
          l_dummy BLOB;
        BEGIN
          select zt_qr.f_qr_as_bmp( l_the_string, lc_quality, 'N' ) qr_code
          into l_dummy
          from dual;
        EXCEPTION
          WHEN others THEN
            dbms_output.put_line( 'error with "' || i || '" characters' );
        END;
      END;
  END LOOP;
END;

Here are some examples for strings only containing the character "A" which produce the problem:

'L'
error with "25" characters
error with "47" characters
error with "77" characters
error with "114" characters
error with "195" characters
error with "224" characters
error with "335" characters
error with "468" characters

'M'
error with "38" characters
error with "61" characters
error with "178" characters
error with "221" characters
error with "262" characters
error with "311" characters
error with "419" characters
error with "483" characters

'Q'
error with "16" characters
error with "29" characters
error with "47" characters
error with "67" characters
error with "108" characters
error with "125" characters
error with "157" characters
error with "189" characters
error with "221" characters
error with "259" characters
error with "352" characters
error with "426" characters
error with "470" characters

'H'
error with "35" characters
error with "50" characters
error with "64" characters
error with "93" characters
error with "143" characters
error with "227" characters
error with "259" characters
error with "321" characters
error with "365" characters
error with "452" characters
error with "493" characters

I am willing to help solve this problem but at this point I don't understand your code enough to do so.

Mutantpenguin commented 3 years ago

Well I might have found something in this and the following 6 lines here: https://github.com/zorantica/plsql-qr-code/blob/fbf3d6fbb8e1cedfbac7ae47b20b945982c40fc1/package/ZT_QR.pkb#L958

It currently looks like this:

    if lnReqNumOfBits - lengthb(p_data) >= 4 then
        lcData := lcData || '0000';
        p_debug('Terminator zeros: 0000', 2);
    else
        lcData := rpad(lcData, lnReqNumOfBits - lengthb(p_data), '0');
        p_debug('Terminator zeros: ' || rpad(null, lnReqNumOfBits - lengthb(p_data), '0'), 2);
    end if;

In here, p_data gets referenced quite a lot, but lcData gets changed. This leads to the initial problem that the length of lcData is highter than lnReqNumOfBits. The LOOP mentioned in my issue description will thus enter an infinit (until the buffer overflows) loop since the check there is only on equality. Apart from that I am pretty sure that the rpad is wrong too. Like this lcData will get overwritten with a very short string most of the time.

I changed it to this in my local version and it seems to work correctly:

      if lnReqNumOfBits - lengthb(lcData) >= 4 then
          lcData := lcData || '0000';
          p_debug('Terminator zeros: 0000', 2);
      else
          lcData := rpad(lcData, lnReqNumOfBits, '0');
          p_debug('Terminator zeros: ' || rpad(null, lnReqNumOfBits, '0'), 2);
      end if;

Of course I don't know if this is the real solution. If this is correct I will create a pull request for you.

zorantica commented 3 years ago

Thank You for discovering this BUG!

And You are right - parameter p_data was used instead of local variable lcData. Also, rpad string length value was problematic. I added some additional debugging and I will prepare new version immediately.

zorantica commented 3 years ago

Fixed! New version is available.

Mutantpenguin commented 3 years ago

You are welcome! And thank you for providing this package.