Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-41904

LZO page compression writes past the compression buffer: 2022 merge regression changed "size = size + LZO work memory" into "size = LZO work memory" in buf_tmp_reserve_compression_buf()

    XMLWordPrintable

Details

    • Bug
    • Status: Needs Feedback (View Workflow)
    • Major
    • Resolution: Unresolved
    • None
    • None
    • None
    • MariaDB source HEAD `c894d97781b5` (13.2.0-alpha)

    Description

      `buf_tmp_reserve_compression_buf()` (`storage/innobase/buf/buf0flu.cc:583-597`) lazily
      allocates one `comp_buf` per I/O slot and never resizes it. Its size is chosen by the
      current provider state only:

      ```c
      ulint size = srv_page_size;
      if (provider_service_lzo->is_loaded)
      size = LZO1X_1_15_MEM_COMPRESS;
      ```

      `LZO1X_1_15_MEM_COMPRESS` is `32768 * sizeof(uchar*)` = 262144 on LP64
      (`include/providers/lzo/lzo1x.h:23`). However, the LZO compressor
      (`storage/innobase/fil/fil0pagecompress.cc:101-112`) writes its output at
      `out_buf + header_len` *and its work memory at `out_buf + srv_page_size`*:

      ```c
      lzo1x_1_15_compress(buf, srv_page_size, out_buf + header_len, &len,
      out_buf + srv_page_size);
      ```

      The required allocation is therefore `srv_page_size + LZO1X_1_15_MEM_COMPRESS`, not
      `LZO1X_1_15_MEM_COMPRESS`. The pre-2022 code computed exactly that
      (`size = size + LZO1X_1_15_MEM_COMPRESS`); the 2022 merge `8ebff3bcb02e` resolved the
      conflict by replacing it with `size = LZO1X_1_15_MEM_COMPRESS`, dropping the
      `+ srv_page_size` part. As a result every LZO page compression writes
      `srv_page_size` bytes past the end of the buffer (and if the slot was first sized while
      `provider_lzo` was not yet loaded, it is only `srv_page_size` long, so the whole 256 KiB
      work-memory area lands past the end).

        1. Steps to reproduce

      Configuration needed (all admin-controlled):

      1. `provider_lzo` loaded (`--plugin-load-add=provider_lzo` or `INSTALL SONAME`), with
      `innodb_compression_algorithm=lzo`; or have the slot allocated before the provider is
      loaded (late `INSTALL SONAME provider_lzo`).
      2. A table in a `PAGE_COMPRESSED=1` tablespace.
      3. Any operation that flushes a compressed page (DML, checkpoint, shutdown).

      Then the LZO compression path runs with the undersized `comp_buf`. A build with ASAN or
      a guarded allocator should report a heap-buffer-overflow write of up to `srv_page_size`
      bytes; without instrumentation the corruption may stay silent for a while.

        1. Expected result

      `comp_buf` must be sized for the full LZO requirement
      (`srv_page_size + LZO1X_1_15_MEM_COMPRESS`) and re-validated if the provider state
      changes after the first allocation. The pre-merge arithmetic documents the intended
      contract.

      Attachments

        Activity

          People

            shipjain Shipra Jain
            csfuzz csfuzz
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:

              Git Integration

                Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.