Skip to content

[C++][Compute] Possible overflow of row table offsets #43202

Description

@zanmato1984

Describe the bug, including details regarding any error messages, version, and platform.

When debugging the test in #43046, I accidentally found that the offsets within the row table were overflowing (which is much worse than #43046 itself). The suspected code is:

uint32_t total_length = to_offsets[num_rows_];
uint32_t total_length_to_append = 0;

Here is a complete test that exposes this issue (note you'll need #43065 to really replicate this issue, otherwise the test crashes first):

// Compare columns to rows of row ids larger than 2^31 within a row table.// Certain AVX2 instructions may behave unexpectedly causing troubles like GH-43046.TEST(KeyCompare, LARGE_MEMORY_TEST(CompareColumnsToRowsMany)) {
ifconstexpr (sizeof(void*) == 4) {
GTEST_SKIP() << "Test only works on 64-bit platforms";
}
// The idea of this case is to create a row table containing one fixed length column and// one var length column (so the row is hence var length and has offset buffer), by// appending the same small batch of n rows repeatedly until it has more than 2^31 rows.// Then compare the last n rows of the row table with the batch.constexprint64_t num_rows_batch = std::numeric_limits<uint16_t>::max();
constexprint64_t num_rows_row_table =
(std::numeric_limits<int32_t>::max() + 1ll) / num_rows_batch * num_rows_batch +
num_rows_batch;
MemoryPool* pool = default_memory_pool();
// The left side columns with num_rows_batch rows.
std::vector<KeyColumnArray> columns_left;
ExecBatch batch_left;
{
std::vector<Datum> values;
// A fixed length array containing random values.ASSERT_OK_AND_ASSIGN(auto value_fixed_length,
::arrow::gen::Random(uint32())->Generate(num_rows_batch));
values.push_back(std::move(value_fixed_length));
// A var length array containing small var length values ("X").ASSERT_OK_AND_ASSIGN(auto value_var_length,
::arrow::gen::Constant(std::make_shared<BinaryScalar>("X"))
->Generate(num_rows_batch));
values.push_back(std::move(value_var_length));
batch_left = ExecBatch(std::move(values), num_rows_batch);
ASSERT_OK(ColumnArraysFromExecBatch(batch_left, &columns_left));
}
// The right side row table with num_rows_row_table rows.
RowTableImpl row_table_right;
{
// Encode the row table with the left columns repeatedly.
std::vector<KeyColumnMetadata> column_metadatas;
ASSERT_OK(ColumnMetadatasFromExecBatch(batch_left, &column_metadatas));
RowTableMetadata table_metadata;
table_metadata.FromColumnMetadataVector(column_metadatas, sizeof(uint64_t),
sizeof(uint64_t));
ASSERT_OK(row_table_right.Init(pool, table_metadata));
RowTableImpl row_table_batch;
ASSERT_OK(row_table_batch.Init(pool, table_metadata));
std::vector<uint16_t> row_ids(num_rows_batch);
std::iota(row_ids.begin(), row_ids.end(), 0);
RowTableEncoder row_encoder;
row_encoder.Init(column_metadatas, sizeof(uint64_t), sizeof(uint64_t));
row_encoder.PrepareEncodeSelected(0, num_rows_batch, columns_left);
ASSERT_OK(row_encoder.EncodeSelected(
&row_table_batch, static_cast<uint32_t>(num_rows_batch), row_ids.data()));
for (int i = 0; i < num_rows_row_table / num_rows_batch; ++i) {
ASSERT_OK(row_table_right.AppendSelectionFrom(row_table_batch, num_rows_batch,
/*source_row_ids=*/NULLPTR));
}
// The row table must contain an offset buffer.ASSERT_NE(row_table_right.offsets(), NULLPTR);
ASSERT_EQ(row_table_right.length(), num_rows_row_table);
}
// The rows to compare: all rows in the batch to the last num_rows_batch rows of the// row table.
std::vector<uint32_t> row_ids_to_compare(num_rows_batch);
std::iota(row_ids_to_compare.begin(), row_ids_to_compare.end(),
num_rows_row_table - num_rows_batch);
TempVectorStack stack;
ASSERT_OK(
stack.Init(pool, KeyCompare::CompareColumnsToRowsTempStackUsage(num_rows_batch)));
LightContext ctx{CpuInfo::GetInstance()->hardware_flags(), &stack};
{
// No selection, output no match row ids.uint32_t num_rows_no_match;
std::vector<uint16_t> row_ids_out(num_rows_batch);
KeyCompare::CompareColumnsToRows(num_rows_batch, /*sel_left_maybe_null=*/NULLPTR,
row_ids_to_compare.data(), &ctx, &num_rows_no_match,
row_ids_out.data(), columns_left, row_table_right,
/*are_cols_in_encoding_order=*/true,
/*out_match_bitvector_maybe_null=*/NULLPTR);
ASSERT_EQ(num_rows_no_match, 0);
}
{
// No selection, output match bit vector.
std::vector<uint8_t> match_bitvector(BytesForBits(num_rows_batch));
KeyCompare::CompareColumnsToRows(
num_rows_batch, /*sel_left_maybe_null=*/NULLPTR, row_ids_to_compare.data(), &ctx,
/*out_num_rows=*/NULLPTR, /*out_sel_left_maybe_same=*/NULLPTR, columns_left,
row_table_right,
/*are_cols_in_encoding_order=*/true, match_bitvector.data());
ASSERT_EQ(arrow::internal::CountSetBits(match_bitvector.data(), 0, num_rows_batch),
num_rows_batch);
}
std::vector<uint16_t> selection_left(num_rows_batch);
std::iota(selection_left.begin(), selection_left.end(), 0);
{
// With selection, output no match row ids.uint32_t num_rows_no_match;
std::vector<uint16_t> row_ids_out(num_rows_batch);
KeyCompare::CompareColumnsToRows(num_rows_batch, selection_left.data(),
row_ids_to_compare.data(), &ctx, &num_rows_no_match,
row_ids_out.data(), columns_left, row_table_right,
/*are_cols_in_encoding_order=*/true,
/*out_match_bitvector_maybe_null=*/NULLPTR);
ASSERT_EQ(num_rows_no_match, 0);
}
{
// With selection, output match bit vector.
std::vector<uint8_t> match_bitvector(BytesForBits(num_rows_batch));
KeyCompare::CompareColumnsToRows(
num_rows_batch, selection_left.data(), row_ids_to_compare.data(), &ctx,
/*out_num_rows=*/NULLPTR, /*out_sel_left_maybe_same=*/NULLPTR, columns_left,
row_table_right,
/*are_cols_in_encoding_order=*/true, match_bitvector.data());
ASSERT_EQ(arrow::internal::CountSetBits(match_bitvector.data(), 0, num_rows_batch),
num_rows_batch);
}
}

Component(s)

C++

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    , 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
     blocks
    (function() {
    function addCopyButtons() {
    document.querySelectorAll('pre code').forEach(function(codeBlock) {
    if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
    codeBlock.parentElement.setAttribute('data-copy-added', 'true');
    var btn = document.createElement('button');
    btn.textContent = 'Copy';
    btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
    btn.onmouseover = function() { this.style.opacity = '1'; };
    btn.onmouseout = function() { this.style.opacity = '0.7'; };
    btn.onclick = function() {
    navigator.clipboard.writeText(codeBlock.textContent).then(function() {
    btn.textContent = 'Copied!';
    setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
    });
    };
    codeBlock.parentElement.style.position = 'relative';
    codeBlock.parentElement.appendChild(btn);
    });
    }
    addCopyButtons();
    // Re-run on dynamic content
    var observer = new MutationObserver(addCopyButtons);
    observer.observe(document.body, { childList: true, subtree: true });
    })();
    }
    } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
    })();
    (function(){
    try {
    var __m = "github.com";
    var __re = new RegExp('^' + "github\\.com" + '
    [C++][Compute] Possible overflow of row table offsets · Issue #43202 · apache/arrow · GitHub
    Skip to content

    [C++][Compute] Possible overflow of row table offsets #43202

    Description

    @zanmato1984

    Describe the bug, including details regarding any error messages, version, and platform.

    When debugging the test in #43046, I accidentally found that the offsets within the row table were overflowing (which is much worse than #43046 itself). The suspected code is:

    uint32_t total_length = to_offsets[num_rows_];
    uint32_t total_length_to_append = 0;

    Here is a complete test that exposes this issue (note you'll need #43065 to really replicate this issue, otherwise the test crashes first):

    // Compare columns to rows of row ids larger than 2^31 within a row table.// Certain AVX2 instructions may behave unexpectedly causing troubles like GH-43046.TEST(KeyCompare, LARGE_MEMORY_TEST(CompareColumnsToRowsMany)) {
    ifconstexpr (sizeof(void*) == 4) {
    GTEST_SKIP() << "Test only works on 64-bit platforms";
    }
    // The idea of this case is to create a row table containing one fixed length column and// one var length column (so the row is hence var length and has offset buffer), by// appending the same small batch of n rows repeatedly until it has more than 2^31 rows.// Then compare the last n rows of the row table with the batch.constexprint64_t num_rows_batch = std::numeric_limits<uint16_t>::max();
    constexprint64_t num_rows_row_table =
    (std::numeric_limits<int32_t>::max() + 1ll) / num_rows_batch * num_rows_batch +
    num_rows_batch;
    MemoryPool* pool = default_memory_pool();
    // The left side columns with num_rows_batch rows.
    std::vector<KeyColumnArray> columns_left;
    ExecBatch batch_left;
    {
    std::vector<Datum> values;
    // A fixed length array containing random values.ASSERT_OK_AND_ASSIGN(auto value_fixed_length,
    ::arrow::gen::Random(uint32())->Generate(num_rows_batch));
    values.push_back(std::move(value_fixed_length));
    // A var length array containing small var length values ("X").ASSERT_OK_AND_ASSIGN(auto value_var_length,
    ::arrow::gen::Constant(std::make_shared<BinaryScalar>("X"))
    ->Generate(num_rows_batch));
    values.push_back(std::move(value_var_length));
    batch_left = ExecBatch(std::move(values), num_rows_batch);
    ASSERT_OK(ColumnArraysFromExecBatch(batch_left, &columns_left));
    }
    // The right side row table with num_rows_row_table rows.
    RowTableImpl row_table_right;
    {
    // Encode the row table with the left columns repeatedly.
    std::vector<KeyColumnMetadata> column_metadatas;
    ASSERT_OK(ColumnMetadatasFromExecBatch(batch_left, &column_metadatas));
    RowTableMetadata table_metadata;
    table_metadata.FromColumnMetadataVector(column_metadatas, sizeof(uint64_t),
    sizeof(uint64_t));
    ASSERT_OK(row_table_right.Init(pool, table_metadata));
    RowTableImpl row_table_batch;
    ASSERT_OK(row_table_batch.Init(pool, table_metadata));
    std::vector<uint16_t> row_ids(num_rows_batch);
    std::iota(row_ids.begin(), row_ids.end(), 0);
    RowTableEncoder row_encoder;
    row_encoder.Init(column_metadatas, sizeof(uint64_t), sizeof(uint64_t));
    row_encoder.PrepareEncodeSelected(0, num_rows_batch, columns_left);
    ASSERT_OK(row_encoder.EncodeSelected(
    &row_table_batch, static_cast<uint32_t>(num_rows_batch), row_ids.data()));
    for (int i = 0; i < num_rows_row_table / num_rows_batch; ++i) {
    ASSERT_OK(row_table_right.AppendSelectionFrom(row_table_batch, num_rows_batch,
    /*source_row_ids=*/NULLPTR));
    }
    // The row table must contain an offset buffer.ASSERT_NE(row_table_right.offsets(), NULLPTR);
    ASSERT_EQ(row_table_right.length(), num_rows_row_table);
    }
    // The rows to compare: all rows in the batch to the last num_rows_batch rows of the// row table.
    std::vector<uint32_t> row_ids_to_compare(num_rows_batch);
    std::iota(row_ids_to_compare.begin(), row_ids_to_compare.end(),
    num_rows_row_table - num_rows_batch);
    TempVectorStack stack;
    ASSERT_OK(
    stack.Init(pool, KeyCompare::CompareColumnsToRowsTempStackUsage(num_rows_batch)));
    LightContext ctx{CpuInfo::GetInstance()->hardware_flags(), &stack};
    {
    // No selection, output no match row ids.uint32_t num_rows_no_match;
    std::vector<uint16_t> row_ids_out(num_rows_batch);
    KeyCompare::CompareColumnsToRows(num_rows_batch, /*sel_left_maybe_null=*/NULLPTR,
    row_ids_to_compare.data(), &ctx, &num_rows_no_match,
    row_ids_out.data(), columns_left, row_table_right,
    /*are_cols_in_encoding_order=*/true,
    /*out_match_bitvector_maybe_null=*/NULLPTR);
    ASSERT_EQ(num_rows_no_match, 0);
    }
    {
    // No selection, output match bit vector.
    std::vector<uint8_t> match_bitvector(BytesForBits(num_rows_batch));
    KeyCompare::CompareColumnsToRows(
    num_rows_batch, /*sel_left_maybe_null=*/NULLPTR, row_ids_to_compare.data(), &ctx,
    /*out_num_rows=*/NULLPTR, /*out_sel_left_maybe_same=*/NULLPTR, columns_left,
    row_table_right,
    /*are_cols_in_encoding_order=*/true, match_bitvector.data());
    ASSERT_EQ(arrow::internal::CountSetBits(match_bitvector.data(), 0, num_rows_batch),
    num_rows_batch);
    }
    std::vector<uint16_t> selection_left(num_rows_batch);
    std::iota(selection_left.begin(), selection_left.end(), 0);
    {
    // With selection, output no match row ids.uint32_t num_rows_no_match;
    std::vector<uint16_t> row_ids_out(num_rows_batch);
    KeyCompare::CompareColumnsToRows(num_rows_batch, selection_left.data(),
    row_ids_to_compare.data(), &ctx, &num_rows_no_match,
    row_ids_out.data(), columns_left, row_table_right,
    /*are_cols_in_encoding_order=*/true,
    /*out_match_bitvector_maybe_null=*/NULLPTR);
    ASSERT_EQ(num_rows_no_match, 0);
    }
    {
    // With selection, output match bit vector.
    std::vector<uint8_t> match_bitvector(BytesForBits(num_rows_batch));
    KeyCompare::CompareColumnsToRows(
    num_rows_batch, selection_left.data(), row_ids_to_compare.data(), &ctx,
    /*out_num_rows=*/NULLPTR, /*out_sel_left_maybe_same=*/NULLPTR, columns_left,
    row_table_right,
    /*are_cols_in_encoding_order=*/true, match_bitvector.data());
    ASSERT_EQ(arrow::internal::CountSetBits(match_bitvector.data(), 0, num_rows_batch),
    num_rows_batch);
    }
    }

    Component(s)

    C++

    Metadata

    Metadata

    Assignees

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [C++][Compute] Possible overflow of row table offsets · Issue #43202 · apache/arrow · GitHub
      Skip to content

      [C++][Compute] Possible overflow of row table offsets #43202

      Description

      @zanmato1984

      Describe the bug, including details regarding any error messages, version, and platform.

      When debugging the test in #43046, I accidentally found that the offsets within the row table were overflowing (which is much worse than #43046 itself). The suspected code is:

      uint32_t total_length = to_offsets[num_rows_];
      uint32_t total_length_to_append = 0;

      Here is a complete test that exposes this issue (note you'll need #43065 to really replicate this issue, otherwise the test crashes first):

      // Compare columns to rows of row ids larger than 2^31 within a row table.// Certain AVX2 instructions may behave unexpectedly causing troubles like GH-43046.TEST(KeyCompare, LARGE_MEMORY_TEST(CompareColumnsToRowsMany)) {
      ifconstexpr (sizeof(void*) == 4) {
      GTEST_SKIP() << "Test only works on 64-bit platforms";
      }
      // The idea of this case is to create a row table containing one fixed length column and// one var length column (so the row is hence var length and has offset buffer), by// appending the same small batch of n rows repeatedly until it has more than 2^31 rows.// Then compare the last n rows of the row table with the batch.constexprint64_t num_rows_batch = std::numeric_limits<uint16_t>::max();
      constexprint64_t num_rows_row_table =
      (std::numeric_limits<int32_t>::max() + 1ll) / num_rows_batch * num_rows_batch +
      num_rows_batch;
      MemoryPool* pool = default_memory_pool();
      // The left side columns with num_rows_batch rows.
      std::vector<KeyColumnArray> columns_left;
      ExecBatch batch_left;
      {
      std::vector<Datum> values;
      // A fixed length array containing random values.ASSERT_OK_AND_ASSIGN(auto value_fixed_length,
      ::arrow::gen::Random(uint32())->Generate(num_rows_batch));
      values.push_back(std::move(value_fixed_length));
      // A var length array containing small var length values ("X").ASSERT_OK_AND_ASSIGN(auto value_var_length,
      ::arrow::gen::Constant(std::make_shared<BinaryScalar>("X"))
      ->Generate(num_rows_batch));
      values.push_back(std::move(value_var_length));
      batch_left = ExecBatch(std::move(values), num_rows_batch);
      ASSERT_OK(ColumnArraysFromExecBatch(batch_left, &columns_left));
      }
      // The right side row table with num_rows_row_table rows.
      RowTableImpl row_table_right;
      {
      // Encode the row table with the left columns repeatedly.
      std::vector<KeyColumnMetadata> column_metadatas;
      ASSERT_OK(ColumnMetadatasFromExecBatch(batch_left, &column_metadatas));
      RowTableMetadata table_metadata;
      table_metadata.FromColumnMetadataVector(column_metadatas, sizeof(uint64_t),
      sizeof(uint64_t));
      ASSERT_OK(row_table_right.Init(pool, table_metadata));
      RowTableImpl row_table_batch;
      ASSERT_OK(row_table_batch.Init(pool, table_metadata));
      std::vector<uint16_t> row_ids(num_rows_batch);
      std::iota(row_ids.begin(), row_ids.end(), 0);
      RowTableEncoder row_encoder;
      row_encoder.Init(column_metadatas, sizeof(uint64_t), sizeof(uint64_t));
      row_encoder.PrepareEncodeSelected(0, num_rows_batch, columns_left);
      ASSERT_OK(row_encoder.EncodeSelected(
      &row_table_batch, static_cast<uint32_t>(num_rows_batch), row_ids.data()));
      for (int i = 0; i < num_rows_row_table / num_rows_batch; ++i) {
      ASSERT_OK(row_table_right.AppendSelectionFrom(row_table_batch, num_rows_batch,
      /*source_row_ids=*/NULLPTR));
      }
      // The row table must contain an offset buffer.ASSERT_NE(row_table_right.offsets(), NULLPTR);
      ASSERT_EQ(row_table_right.length(), num_rows_row_table);
      }
      // The rows to compare: all rows in the batch to the last num_rows_batch rows of the// row table.
      std::vector<uint32_t> row_ids_to_compare(num_rows_batch);
      std::iota(row_ids_to_compare.begin(), row_ids_to_compare.end(),
      num_rows_row_table - num_rows_batch);
      TempVectorStack stack;
      ASSERT_OK(
      stack.Init(pool, KeyCompare::CompareColumnsToRowsTempStackUsage(num_rows_batch)));
      LightContext ctx{CpuInfo::GetInstance()->hardware_flags(), &stack};
      {
      // No selection, output no match row ids.uint32_t num_rows_no_match;
      std::vector<uint16_t> row_ids_out(num_rows_batch);
      KeyCompare::CompareColumnsToRows(num_rows_batch, /*sel_left_maybe_null=*/NULLPTR,
      row_ids_to_compare.data(), &ctx, &num_rows_no_match,
      row_ids_out.data(), columns_left, row_table_right,
      /*are_cols_in_encoding_order=*/true,
      /*out_match_bitvector_maybe_null=*/NULLPTR);
      ASSERT_EQ(num_rows_no_match, 0);
      }
      {
      // No selection, output match bit vector.
      std::vector<uint8_t> match_bitvector(BytesForBits(num_rows_batch));
      KeyCompare::CompareColumnsToRows(
      num_rows_batch, /*sel_left_maybe_null=*/NULLPTR, row_ids_to_compare.data(), &ctx,
      /*out_num_rows=*/NULLPTR, /*out_sel_left_maybe_same=*/NULLPTR, columns_left,
      row_table_right,
      /*are_cols_in_encoding_order=*/true, match_bitvector.data());
      ASSERT_EQ(arrow::internal::CountSetBits(match_bitvector.data(), 0, num_rows_batch),
      num_rows_batch);
      }
      std::vector<uint16_t> selection_left(num_rows_batch);
      std::iota(selection_left.begin(), selection_left.end(), 0);
      {
      // With selection, output no match row ids.uint32_t num_rows_no_match;
      std::vector<uint16_t> row_ids_out(num_rows_batch);
      KeyCompare::CompareColumnsToRows(num_rows_batch, selection_left.data(),
      row_ids_to_compare.data(), &ctx, &num_rows_no_match,
      row_ids_out.data(), columns_left, row_table_right,
      /*are_cols_in_encoding_order=*/true,
      /*out_match_bitvector_maybe_null=*/NULLPTR);
      ASSERT_EQ(num_rows_no_match, 0);
      }
      {
      // With selection, output match bit vector.
      std::vector<uint8_t> match_bitvector(BytesForBits(num_rows_batch));
      KeyCompare::CompareColumnsToRows(
      num_rows_batch, selection_left.data(), row_ids_to_compare.data(), &ctx,
      /*out_num_rows=*/NULLPTR, /*out_sel_left_maybe_same=*/NULLPTR, columns_left,
      row_table_right,
      /*are_cols_in_encoding_order=*/true, match_bitvector.data());
      ASSERT_EQ(arrow::internal::CountSetBits(match_bitvector.data(), 0, num_rows_batch),
      num_rows_batch);
      }
      }

      Component(s)

      C++

      Metadata

      Metadata

      Assignees

      Type

      No type

      Projects

      No projects

        Milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions

        , 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [C++][Compute] Possible overflow of row table offsets · Issue #43202 · apache/arrow · GitHub
        Skip to content

        [C++][Compute] Possible overflow of row table offsets #43202

        Description

        @zanmato1984

        Describe the bug, including details regarding any error messages, version, and platform.

        When debugging the test in #43046, I accidentally found that the offsets within the row table were overflowing (which is much worse than #43046 itself). The suspected code is:

        uint32_t total_length = to_offsets[num_rows_];
        uint32_t total_length_to_append = 0;

        Here is a complete test that exposes this issue (note you'll need #43065 to really replicate this issue, otherwise the test crashes first):

        // Compare columns to rows of row ids larger than 2^31 within a row table.// Certain AVX2 instructions may behave unexpectedly causing troubles like GH-43046.TEST(KeyCompare, LARGE_MEMORY_TEST(CompareColumnsToRowsMany)) {
        ifconstexpr (sizeof(void*) == 4) {
        GTEST_SKIP() << "Test only works on 64-bit platforms";
        }
        // The idea of this case is to create a row table containing one fixed length column and// one var length column (so the row is hence var length and has offset buffer), by// appending the same small batch of n rows repeatedly until it has more than 2^31 rows.// Then compare the last n rows of the row table with the batch.constexprint64_t num_rows_batch = std::numeric_limits<uint16_t>::max();
        constexprint64_t num_rows_row_table =
        (std::numeric_limits<int32_t>::max() + 1ll) / num_rows_batch * num_rows_batch +
        num_rows_batch;
        MemoryPool* pool = default_memory_pool();
        // The left side columns with num_rows_batch rows.
        std::vector<KeyColumnArray> columns_left;
        ExecBatch batch_left;
        {
        std::vector<Datum> values;
        // A fixed length array containing random values.ASSERT_OK_AND_ASSIGN(auto value_fixed_length,
        ::arrow::gen::Random(uint32())->Generate(num_rows_batch));
        values.push_back(std::move(value_fixed_length));
        // A var length array containing small var length values ("X").ASSERT_OK_AND_ASSIGN(auto value_var_length,
        ::arrow::gen::Constant(std::make_shared<BinaryScalar>("X"))
        ->Generate(num_rows_batch));
        values.push_back(std::move(value_var_length));
        batch_left = ExecBatch(std::move(values), num_rows_batch);
        ASSERT_OK(ColumnArraysFromExecBatch(batch_left, &columns_left));
        }
        // The right side row table with num_rows_row_table rows.
        RowTableImpl row_table_right;
        {
        // Encode the row table with the left columns repeatedly.
        std::vector<KeyColumnMetadata> column_metadatas;
        ASSERT_OK(ColumnMetadatasFromExecBatch(batch_left, &column_metadatas));
        RowTableMetadata table_metadata;
        table_metadata.FromColumnMetadataVector(column_metadatas, sizeof(uint64_t),
        sizeof(uint64_t));
        ASSERT_OK(row_table_right.Init(pool, table_metadata));
        RowTableImpl row_table_batch;
        ASSERT_OK(row_table_batch.Init(pool, table_metadata));
        std::vector<uint16_t> row_ids(num_rows_batch);
        std::iota(row_ids.begin(), row_ids.end(), 0);
        RowTableEncoder row_encoder;
        row_encoder.Init(column_metadatas, sizeof(uint64_t), sizeof(uint64_t));
        row_encoder.PrepareEncodeSelected(0, num_rows_batch, columns_left);
        ASSERT_OK(row_encoder.EncodeSelected(
        &row_table_batch, static_cast<uint32_t>(num_rows_batch), row_ids.data()));
        for (int i = 0; i < num_rows_row_table / num_rows_batch; ++i) {
        ASSERT_OK(row_table_right.AppendSelectionFrom(row_table_batch, num_rows_batch,
        /*source_row_ids=*/NULLPTR));
        }
        // The row table must contain an offset buffer.ASSERT_NE(row_table_right.offsets(), NULLPTR);
        ASSERT_EQ(row_table_right.length(), num_rows_row_table);
        }
        // The rows to compare: all rows in the batch to the last num_rows_batch rows of the// row table.
        std::vector<uint32_t> row_ids_to_compare(num_rows_batch);
        std::iota(row_ids_to_compare.begin(), row_ids_to_compare.end(),
        num_rows_row_table - num_rows_batch);
        TempVectorStack stack;
        ASSERT_OK(
        stack.Init(pool, KeyCompare::CompareColumnsToRowsTempStackUsage(num_rows_batch)));
        LightContext ctx{CpuInfo::GetInstance()->hardware_flags(), &stack};
        {
        // No selection, output no match row ids.uint32_t num_rows_no_match;
        std::vector<uint16_t> row_ids_out(num_rows_batch);
        KeyCompare::CompareColumnsToRows(num_rows_batch, /*sel_left_maybe_null=*/NULLPTR,
        row_ids_to_compare.data(), &ctx, &num_rows_no_match,
        row_ids_out.data(), columns_left, row_table_right,
        /*are_cols_in_encoding_order=*/true,
        /*out_match_bitvector_maybe_null=*/NULLPTR);
        ASSERT_EQ(num_rows_no_match, 0);
        }
        {
        // No selection, output match bit vector.
        std::vector<uint8_t> match_bitvector(BytesForBits(num_rows_batch));
        KeyCompare::CompareColumnsToRows(
        num_rows_batch, /*sel_left_maybe_null=*/NULLPTR, row_ids_to_compare.data(), &ctx,
        /*out_num_rows=*/NULLPTR, /*out_sel_left_maybe_same=*/NULLPTR, columns_left,
        row_table_right,
        /*are_cols_in_encoding_order=*/true, match_bitvector.data());
        ASSERT_EQ(arrow::internal::CountSetBits(match_bitvector.data(), 0, num_rows_batch),
        num_rows_batch);
        }
        std::vector<uint16_t> selection_left(num_rows_batch);
        std::iota(selection_left.begin(), selection_left.end(), 0);
        {
        // With selection, output no match row ids.uint32_t num_rows_no_match;
        std::vector<uint16_t> row_ids_out(num_rows_batch);
        KeyCompare::CompareColumnsToRows(num_rows_batch, selection_left.data(),
        row_ids_to_compare.data(), &ctx, &num_rows_no_match,
        row_ids_out.data(), columns_left, row_table_right,
        /*are_cols_in_encoding_order=*/true,
        /*out_match_bitvector_maybe_null=*/NULLPTR);
        ASSERT_EQ(num_rows_no_match, 0);
        }
        {
        // With selection, output match bit vector.
        std::vector<uint8_t> match_bitvector(BytesForBits(num_rows_batch));
        KeyCompare::CompareColumnsToRows(
        num_rows_batch, selection_left.data(), row_ids_to_compare.data(), &ctx,
        /*out_num_rows=*/NULLPTR, /*out_sel_left_maybe_same=*/NULLPTR, columns_left,
        row_table_right,
        /*are_cols_in_encoding_order=*/true, match_bitvector.data());
        ASSERT_EQ(arrow::internal::CountSetBits(match_bitvector.data(), 0, num_rows_batch),
        num_rows_batch);
        }
        }

        Component(s)

        C++

        Metadata

        Metadata

        Assignees

        Type

        No type

        Projects

        No projects

          Milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' [C++][Compute] Possible overflow of row table offsets · Issue #43202 · apache/arrow · GitHub
          Skip to content

          [C++][Compute] Possible overflow of row table offsets #43202

          Description

          @zanmato1984

          Describe the bug, including details regarding any error messages, version, and platform.

          When debugging the test in #43046, I accidentally found that the offsets within the row table were overflowing (which is much worse than #43046 itself). The suspected code is:

          uint32_t total_length = to_offsets[num_rows_];
          uint32_t total_length_to_append = 0;

          Here is a complete test that exposes this issue (note you'll need #43065 to really replicate this issue, otherwise the test crashes first):

          // Compare columns to rows of row ids larger than 2^31 within a row table.// Certain AVX2 instructions may behave unexpectedly causing troubles like GH-43046.TEST(KeyCompare, LARGE_MEMORY_TEST(CompareColumnsToRowsMany)) {
          ifconstexpr (sizeof(void*) == 4) {
          GTEST_SKIP() << "Test only works on 64-bit platforms";
          }
          // The idea of this case is to create a row table containing one fixed length column and// one var length column (so the row is hence var length and has offset buffer), by// appending the same small batch of n rows repeatedly until it has more than 2^31 rows.// Then compare the last n rows of the row table with the batch.constexprint64_t num_rows_batch = std::numeric_limits<uint16_t>::max();
          constexprint64_t num_rows_row_table =
          (std::numeric_limits<int32_t>::max() + 1ll) / num_rows_batch * num_rows_batch +
          num_rows_batch;
          MemoryPool* pool = default_memory_pool();
          // The left side columns with num_rows_batch rows.
          std::vector<KeyColumnArray> columns_left;
          ExecBatch batch_left;
          {
          std::vector<Datum> values;
          // A fixed length array containing random values.ASSERT_OK_AND_ASSIGN(auto value_fixed_length,
          ::arrow::gen::Random(uint32())->Generate(num_rows_batch));
          values.push_back(std::move(value_fixed_length));
          // A var length array containing small var length values ("X").ASSERT_OK_AND_ASSIGN(auto value_var_length,
          ::arrow::gen::Constant(std::make_shared<BinaryScalar>("X"))
          ->Generate(num_rows_batch));
          values.push_back(std::move(value_var_length));
          batch_left = ExecBatch(std::move(values), num_rows_batch);
          ASSERT_OK(ColumnArraysFromExecBatch(batch_left, &columns_left));
          }
          // The right side row table with num_rows_row_table rows.
          RowTableImpl row_table_right;
          {
          // Encode the row table with the left columns repeatedly.
          std::vector<KeyColumnMetadata> column_metadatas;
          ASSERT_OK(ColumnMetadatasFromExecBatch(batch_left, &column_metadatas));
          RowTableMetadata table_metadata;
          table_metadata.FromColumnMetadataVector(column_metadatas, sizeof(uint64_t),
          sizeof(uint64_t));
          ASSERT_OK(row_table_right.Init(pool, table_metadata));
          RowTableImpl row_table_batch;
          ASSERT_OK(row_table_batch.Init(pool, table_metadata));
          std::vector<uint16_t> row_ids(num_rows_batch);
          std::iota(row_ids.begin(), row_ids.end(), 0);
          RowTableEncoder row_encoder;
          row_encoder.Init(column_metadatas, sizeof(uint64_t), sizeof(uint64_t));
          row_encoder.PrepareEncodeSelected(0, num_rows_batch, columns_left);
          ASSERT_OK(row_encoder.EncodeSelected(
          &row_table_batch, static_cast<uint32_t>(num_rows_batch), row_ids.data()));
          for (int i = 0; i < num_rows_row_table / num_rows_batch; ++i) {
          ASSERT_OK(row_table_right.AppendSelectionFrom(row_table_batch, num_rows_batch,
          /*source_row_ids=*/NULLPTR));
          }
          // The row table must contain an offset buffer.ASSERT_NE(row_table_right.offsets(), NULLPTR);
          ASSERT_EQ(row_table_right.length(), num_rows_row_table);
          }
          // The rows to compare: all rows in the batch to the last num_rows_batch rows of the// row table.
          std::vector<uint32_t> row_ids_to_compare(num_rows_batch);
          std::iota(row_ids_to_compare.begin(), row_ids_to_compare.end(),
          num_rows_row_table - num_rows_batch);
          TempVectorStack stack;
          ASSERT_OK(
          stack.Init(pool, KeyCompare::CompareColumnsToRowsTempStackUsage(num_rows_batch)));
          LightContext ctx{CpuInfo::GetInstance()->hardware_flags(), &stack};
          {
          // No selection, output no match row ids.uint32_t num_rows_no_match;
          std::vector<uint16_t> row_ids_out(num_rows_batch);
          KeyCompare::CompareColumnsToRows(num_rows_batch, /*sel_left_maybe_null=*/NULLPTR,
          row_ids_to_compare.data(), &ctx, &num_rows_no_match,
          row_ids_out.data(), columns_left, row_table_right,
          /*are_cols_in_encoding_order=*/true,
          /*out_match_bitvector_maybe_null=*/NULLPTR);
          ASSERT_EQ(num_rows_no_match, 0);
          }
          {
          // No selection, output match bit vector.
          std::vector<uint8_t> match_bitvector(BytesForBits(num_rows_batch));
          KeyCompare::CompareColumnsToRows(
          num_rows_batch, /*sel_left_maybe_null=*/NULLPTR, row_ids_to_compare.data(), &ctx,
          /*out_num_rows=*/NULLPTR, /*out_sel_left_maybe_same=*/NULLPTR, columns_left,
          row_table_right,
          /*are_cols_in_encoding_order=*/true, match_bitvector.data());
          ASSERT_EQ(arrow::internal::CountSetBits(match_bitvector.data(), 0, num_rows_batch),
          num_rows_batch);
          }
          std::vector<uint16_t> selection_left(num_rows_batch);
          std::iota(selection_left.begin(), selection_left.end(), 0);
          {
          // With selection, output no match row ids.uint32_t num_rows_no_match;
          std::vector<uint16_t> row_ids_out(num_rows_batch);
          KeyCompare::CompareColumnsToRows(num_rows_batch, selection_left.data(),
          row_ids_to_compare.data(), &ctx, &num_rows_no_match,
          row_ids_out.data(), columns_left, row_table_right,
          /*are_cols_in_encoding_order=*/true,
          /*out_match_bitvector_maybe_null=*/NULLPTR);
          ASSERT_EQ(num_rows_no_match, 0);
          }
          {
          // With selection, output match bit vector.
          std::vector<uint8_t> match_bitvector(BytesForBits(num_rows_batch));
          KeyCompare::CompareColumnsToRows(
          num_rows_batch, selection_left.data(), row_ids_to_compare.data(), &ctx,
          /*out_num_rows=*/NULLPTR, /*out_sel_left_maybe_same=*/NULLPTR, columns_left,
          row_table_right,
          /*are_cols_in_encoding_order=*/true, match_bitvector.data());
          ASSERT_EQ(arrow::internal::CountSetBits(match_bitvector.data(), 0, num_rows_batch),
          num_rows_batch);
          }
          }

          Component(s)

          C++

          Metadata

          Metadata

          Assignees

          Type

          No type

          Projects

          No projects

            Milestone

            Relationships

            None yet

            Development

            No branches or pull requests

            Issue actions

            , 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [C++][Compute] Possible overflow of row table offsets · Issue #43202 · apache/arrow · GitHub
            Skip to content

            [C++][Compute] Possible overflow of row table offsets #43202

            Description

            @zanmato1984

            Describe the bug, including details regarding any error messages, version, and platform.

            When debugging the test in #43046, I accidentally found that the offsets within the row table were overflowing (which is much worse than #43046 itself). The suspected code is:

            uint32_t total_length = to_offsets[num_rows_];
            uint32_t total_length_to_append = 0;

            Here is a complete test that exposes this issue (note you'll need #43065 to really replicate this issue, otherwise the test crashes first):

            // Compare columns to rows of row ids larger than 2^31 within a row table.// Certain AVX2 instructions may behave unexpectedly causing troubles like GH-43046.TEST(KeyCompare, LARGE_MEMORY_TEST(CompareColumnsToRowsMany)) {
            ifconstexpr (sizeof(void*) == 4) {
            GTEST_SKIP() << "Test only works on 64-bit platforms";
            }
            // The idea of this case is to create a row table containing one fixed length column and// one var length column (so the row is hence var length and has offset buffer), by// appending the same small batch of n rows repeatedly until it has more than 2^31 rows.// Then compare the last n rows of the row table with the batch.constexprint64_t num_rows_batch = std::numeric_limits<uint16_t>::max();
            constexprint64_t num_rows_row_table =
            (std::numeric_limits<int32_t>::max() + 1ll) / num_rows_batch * num_rows_batch +
            num_rows_batch;
            MemoryPool* pool = default_memory_pool();
            // The left side columns with num_rows_batch rows.
            std::vector<KeyColumnArray> columns_left;
            ExecBatch batch_left;
            {
            std::vector<Datum> values;
            // A fixed length array containing random values.ASSERT_OK_AND_ASSIGN(auto value_fixed_length,
            ::arrow::gen::Random(uint32())->Generate(num_rows_batch));
            values.push_back(std::move(value_fixed_length));
            // A var length array containing small var length values ("X").ASSERT_OK_AND_ASSIGN(auto value_var_length,
            ::arrow::gen::Constant(std::make_shared<BinaryScalar>("X"))
            ->Generate(num_rows_batch));
            values.push_back(std::move(value_var_length));
            batch_left = ExecBatch(std::move(values), num_rows_batch);
            ASSERT_OK(ColumnArraysFromExecBatch(batch_left, &columns_left));
            }
            // The right side row table with num_rows_row_table rows.
            RowTableImpl row_table_right;
            {
            // Encode the row table with the left columns repeatedly.
            std::vector<KeyColumnMetadata> column_metadatas;
            ASSERT_OK(ColumnMetadatasFromExecBatch(batch_left, &column_metadatas));
            RowTableMetadata table_metadata;
            table_metadata.FromColumnMetadataVector(column_metadatas, sizeof(uint64_t),
            sizeof(uint64_t));
            ASSERT_OK(row_table_right.Init(pool, table_metadata));
            RowTableImpl row_table_batch;
            ASSERT_OK(row_table_batch.Init(pool, table_metadata));
            std::vector<uint16_t> row_ids(num_rows_batch);
            std::iota(row_ids.begin(), row_ids.end(), 0);
            RowTableEncoder row_encoder;
            row_encoder.Init(column_metadatas, sizeof(uint64_t), sizeof(uint64_t));
            row_encoder.PrepareEncodeSelected(0, num_rows_batch, columns_left);
            ASSERT_OK(row_encoder.EncodeSelected(
            &row_table_batch, static_cast<uint32_t>(num_rows_batch), row_ids.data()));
            for (int i = 0; i < num_rows_row_table / num_rows_batch; ++i) {
            ASSERT_OK(row_table_right.AppendSelectionFrom(row_table_batch, num_rows_batch,
            /*source_row_ids=*/NULLPTR));
            }
            // The row table must contain an offset buffer.ASSERT_NE(row_table_right.offsets(), NULLPTR);
            ASSERT_EQ(row_table_right.length(), num_rows_row_table);
            }
            // The rows to compare: all rows in the batch to the last num_rows_batch rows of the// row table.
            std::vector<uint32_t> row_ids_to_compare(num_rows_batch);
            std::iota(row_ids_to_compare.begin(), row_ids_to_compare.end(),
            num_rows_row_table - num_rows_batch);
            TempVectorStack stack;
            ASSERT_OK(
            stack.Init(pool, KeyCompare::CompareColumnsToRowsTempStackUsage(num_rows_batch)));
            LightContext ctx{CpuInfo::GetInstance()->hardware_flags(), &stack};
            {
            // No selection, output no match row ids.uint32_t num_rows_no_match;
            std::vector<uint16_t> row_ids_out(num_rows_batch);
            KeyCompare::CompareColumnsToRows(num_rows_batch, /*sel_left_maybe_null=*/NULLPTR,
            row_ids_to_compare.data(), &ctx, &num_rows_no_match,
            row_ids_out.data(), columns_left, row_table_right,
            /*are_cols_in_encoding_order=*/true,
            /*out_match_bitvector_maybe_null=*/NULLPTR);
            ASSERT_EQ(num_rows_no_match, 0);
            }
            {
            // No selection, output match bit vector.
            std::vector<uint8_t> match_bitvector(BytesForBits(num_rows_batch));
            KeyCompare::CompareColumnsToRows(
            num_rows_batch, /*sel_left_maybe_null=*/NULLPTR, row_ids_to_compare.data(), &ctx,
            /*out_num_rows=*/NULLPTR, /*out_sel_left_maybe_same=*/NULLPTR, columns_left,
            row_table_right,
            /*are_cols_in_encoding_order=*/true, match_bitvector.data());
            ASSERT_EQ(arrow::internal::CountSetBits(match_bitvector.data(), 0, num_rows_batch),
            num_rows_batch);
            }
            std::vector<uint16_t> selection_left(num_rows_batch);
            std::iota(selection_left.begin(), selection_left.end(), 0);
            {
            // With selection, output no match row ids.uint32_t num_rows_no_match;
            std::vector<uint16_t> row_ids_out(num_rows_batch);
            KeyCompare::CompareColumnsToRows(num_rows_batch, selection_left.data(),
            row_ids_to_compare.data(), &ctx, &num_rows_no_match,
            row_ids_out.data(), columns_left, row_table_right,
            /*are_cols_in_encoding_order=*/true,
            /*out_match_bitvector_maybe_null=*/NULLPTR);
            ASSERT_EQ(num_rows_no_match, 0);
            }
            {
            // With selection, output match bit vector.
            std::vector<uint8_t> match_bitvector(BytesForBits(num_rows_batch));
            KeyCompare::CompareColumnsToRows(
            num_rows_batch, selection_left.data(), row_ids_to_compare.data(), &ctx,
            /*out_num_rows=*/NULLPTR, /*out_sel_left_maybe_same=*/NULLPTR, columns_left,
            row_table_right,
            /*are_cols_in_encoding_order=*/true, match_bitvector.data());
            ASSERT_EQ(arrow::internal::CountSetBits(match_bitvector.data(), 0, num_rows_batch),
            num_rows_batch);
            }
            }

            Component(s)

            C++

            Metadata

            Metadata

            Assignees

            Type

            No type

            Projects

            No projects

              Milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [C++][Compute] Possible overflow of row table offsets · Issue #43202 · apache/arrow · GitHub
              Skip to content

              [C++][Compute] Possible overflow of row table offsets #43202

              Description

              @zanmato1984

              Describe the bug, including details regarding any error messages, version, and platform.

              When debugging the test in #43046, I accidentally found that the offsets within the row table were overflowing (which is much worse than #43046 itself). The suspected code is:

              uint32_t total_length = to_offsets[num_rows_];
              uint32_t total_length_to_append = 0;

              Here is a complete test that exposes this issue (note you'll need #43065 to really replicate this issue, otherwise the test crashes first):

              // Compare columns to rows of row ids larger than 2^31 within a row table.// Certain AVX2 instructions may behave unexpectedly causing troubles like GH-43046.TEST(KeyCompare, LARGE_MEMORY_TEST(CompareColumnsToRowsMany)) {
              ifconstexpr (sizeof(void*) == 4) {
              GTEST_SKIP() << "Test only works on 64-bit platforms";
              }
              // The idea of this case is to create a row table containing one fixed length column and// one var length column (so the row is hence var length and has offset buffer), by// appending the same small batch of n rows repeatedly until it has more than 2^31 rows.// Then compare the last n rows of the row table with the batch.constexprint64_t num_rows_batch = std::numeric_limits<uint16_t>::max();
              constexprint64_t num_rows_row_table =
              (std::numeric_limits<int32_t>::max() + 1ll) / num_rows_batch * num_rows_batch +
              num_rows_batch;
              MemoryPool* pool = default_memory_pool();
              // The left side columns with num_rows_batch rows.
              std::vector<KeyColumnArray> columns_left;
              ExecBatch batch_left;
              {
              std::vector<Datum> values;
              // A fixed length array containing random values.ASSERT_OK_AND_ASSIGN(auto value_fixed_length,
              ::arrow::gen::Random(uint32())->Generate(num_rows_batch));
              values.push_back(std::move(value_fixed_length));
              // A var length array containing small var length values ("X").ASSERT_OK_AND_ASSIGN(auto value_var_length,
              ::arrow::gen::Constant(std::make_shared<BinaryScalar>("X"))
              ->Generate(num_rows_batch));
              values.push_back(std::move(value_var_length));
              batch_left = ExecBatch(std::move(values), num_rows_batch);
              ASSERT_OK(ColumnArraysFromExecBatch(batch_left, &columns_left));
              }
              // The right side row table with num_rows_row_table rows.
              RowTableImpl row_table_right;
              {
              // Encode the row table with the left columns repeatedly.
              std::vector<KeyColumnMetadata> column_metadatas;
              ASSERT_OK(ColumnMetadatasFromExecBatch(batch_left, &column_metadatas));
              RowTableMetadata table_metadata;
              table_metadata.FromColumnMetadataVector(column_metadatas, sizeof(uint64_t),
              sizeof(uint64_t));
              ASSERT_OK(row_table_right.Init(pool, table_metadata));
              RowTableImpl row_table_batch;
              ASSERT_OK(row_table_batch.Init(pool, table_metadata));
              std::vector<uint16_t> row_ids(num_rows_batch);
              std::iota(row_ids.begin(), row_ids.end(), 0);
              RowTableEncoder row_encoder;
              row_encoder.Init(column_metadatas, sizeof(uint64_t), sizeof(uint64_t));
              row_encoder.PrepareEncodeSelected(0, num_rows_batch, columns_left);
              ASSERT_OK(row_encoder.EncodeSelected(
              &row_table_batch, static_cast<uint32_t>(num_rows_batch), row_ids.data()));
              for (int i = 0; i < num_rows_row_table / num_rows_batch; ++i) {
              ASSERT_OK(row_table_right.AppendSelectionFrom(row_table_batch, num_rows_batch,
              /*source_row_ids=*/NULLPTR));
              }
              // The row table must contain an offset buffer.ASSERT_NE(row_table_right.offsets(), NULLPTR);
              ASSERT_EQ(row_table_right.length(), num_rows_row_table);
              }
              // The rows to compare: all rows in the batch to the last num_rows_batch rows of the// row table.
              std::vector<uint32_t> row_ids_to_compare(num_rows_batch);
              std::iota(row_ids_to_compare.begin(), row_ids_to_compare.end(),
              num_rows_row_table - num_rows_batch);
              TempVectorStack stack;
              ASSERT_OK(
              stack.Init(pool, KeyCompare::CompareColumnsToRowsTempStackUsage(num_rows_batch)));
              LightContext ctx{CpuInfo::GetInstance()->hardware_flags(), &stack};
              {
              // No selection, output no match row ids.uint32_t num_rows_no_match;
              std::vector<uint16_t> row_ids_out(num_rows_batch);
              KeyCompare::CompareColumnsToRows(num_rows_batch, /*sel_left_maybe_null=*/NULLPTR,
              row_ids_to_compare.data(), &ctx, &num_rows_no_match,
              row_ids_out.data(), columns_left, row_table_right,
              /*are_cols_in_encoding_order=*/true,
              /*out_match_bitvector_maybe_null=*/NULLPTR);
              ASSERT_EQ(num_rows_no_match, 0);
              }
              {
              // No selection, output match bit vector.
              std::vector<uint8_t> match_bitvector(BytesForBits(num_rows_batch));
              KeyCompare::CompareColumnsToRows(
              num_rows_batch, /*sel_left_maybe_null=*/NULLPTR, row_ids_to_compare.data(), &ctx,
              /*out_num_rows=*/NULLPTR, /*out_sel_left_maybe_same=*/NULLPTR, columns_left,
              row_table_right,
              /*are_cols_in_encoding_order=*/true, match_bitvector.data());
              ASSERT_EQ(arrow::internal::CountSetBits(match_bitvector.data(), 0, num_rows_batch),
              num_rows_batch);
              }
              std::vector<uint16_t> selection_left(num_rows_batch);
              std::iota(selection_left.begin(), selection_left.end(), 0);
              {
              // With selection, output no match row ids.uint32_t num_rows_no_match;
              std::vector<uint16_t> row_ids_out(num_rows_batch);
              KeyCompare::CompareColumnsToRows(num_rows_batch, selection_left.data(),
              row_ids_to_compare.data(), &ctx, &num_rows_no_match,
              row_ids_out.data(), columns_left, row_table_right,
              /*are_cols_in_encoding_order=*/true,
              /*out_match_bitvector_maybe_null=*/NULLPTR);
              ASSERT_EQ(num_rows_no_match, 0);
              }
              {
              // With selection, output match bit vector.
              std::vector<uint8_t> match_bitvector(BytesForBits(num_rows_batch));
              KeyCompare::CompareColumnsToRows(
              num_rows_batch, selection_left.data(), row_ids_to_compare.data(), &ctx,
              /*out_num_rows=*/NULLPTR, /*out_sel_left_maybe_same=*/NULLPTR, columns_left,
              row_table_right,
              /*are_cols_in_encoding_order=*/true, match_bitvector.data());
              ASSERT_EQ(arrow::internal::CountSetBits(match_bitvector.data(), 0, num_rows_batch),
              num_rows_batch);
              }
              }

              Component(s)

              C++

              Metadata

              Metadata

              Assignees

              Type

              No type

              Projects

              No projects

                Milestone

                Relationships

                None yet

                Development

                No branches or pull requests

                Issue actions

                , 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); [C++][Compute] Possible overflow of row table offsets · Issue #43202 · apache/arrow · GitHub
                Skip to content

                [C++][Compute] Possible overflow of row table offsets #43202

                Description

                @zanmato1984

                Describe the bug, including details regarding any error messages, version, and platform.

                When debugging the test in #43046, I accidentally found that the offsets within the row table were overflowing (which is much worse than #43046 itself). The suspected code is:

                uint32_t total_length = to_offsets[num_rows_];
                uint32_t total_length_to_append = 0;

                Here is a complete test that exposes this issue (note you'll need #43065 to really replicate this issue, otherwise the test crashes first):

                // Compare columns to rows of row ids larger than 2^31 within a row table.// Certain AVX2 instructions may behave unexpectedly causing troubles like GH-43046.TEST(KeyCompare, LARGE_MEMORY_TEST(CompareColumnsToRowsMany)) {
                ifconstexpr (sizeof(void*) == 4) {
                GTEST_SKIP() << "Test only works on 64-bit platforms";
                }
                // The idea of this case is to create a row table containing one fixed length column and// one var length column (so the row is hence var length and has offset buffer), by// appending the same small batch of n rows repeatedly until it has more than 2^31 rows.// Then compare the last n rows of the row table with the batch.constexprint64_t num_rows_batch = std::numeric_limits<uint16_t>::max();
                constexprint64_t num_rows_row_table =
                (std::numeric_limits<int32_t>::max() + 1ll) / num_rows_batch * num_rows_batch +
                num_rows_batch;
                MemoryPool* pool = default_memory_pool();
                // The left side columns with num_rows_batch rows.
                std::vector<KeyColumnArray> columns_left;
                ExecBatch batch_left;
                {
                std::vector<Datum> values;
                // A fixed length array containing random values.ASSERT_OK_AND_ASSIGN(auto value_fixed_length,
                ::arrow::gen::Random(uint32())->Generate(num_rows_batch));
                values.push_back(std::move(value_fixed_length));
                // A var length array containing small var length values ("X").ASSERT_OK_AND_ASSIGN(auto value_var_length,
                ::arrow::gen::Constant(std::make_shared<BinaryScalar>("X"))
                ->Generate(num_rows_batch));
                values.push_back(std::move(value_var_length));
                batch_left = ExecBatch(std::move(values), num_rows_batch);
                ASSERT_OK(ColumnArraysFromExecBatch(batch_left, &columns_left));
                }
                // The right side row table with num_rows_row_table rows.
                RowTableImpl row_table_right;
                {
                // Encode the row table with the left columns repeatedly.
                std::vector<KeyColumnMetadata> column_metadatas;
                ASSERT_OK(ColumnMetadatasFromExecBatch(batch_left, &column_metadatas));
                RowTableMetadata table_metadata;
                table_metadata.FromColumnMetadataVector(column_metadatas, sizeof(uint64_t),
                sizeof(uint64_t));
                ASSERT_OK(row_table_right.Init(pool, table_metadata));
                RowTableImpl row_table_batch;
                ASSERT_OK(row_table_batch.Init(pool, table_metadata));
                std::vector<uint16_t> row_ids(num_rows_batch);
                std::iota(row_ids.begin(), row_ids.end(), 0);
                RowTableEncoder row_encoder;
                row_encoder.Init(column_metadatas, sizeof(uint64_t), sizeof(uint64_t));
                row_encoder.PrepareEncodeSelected(0, num_rows_batch, columns_left);
                ASSERT_OK(row_encoder.EncodeSelected(
                &row_table_batch, static_cast<uint32_t>(num_rows_batch), row_ids.data()));
                for (int i = 0; i < num_rows_row_table / num_rows_batch; ++i) {
                ASSERT_OK(row_table_right.AppendSelectionFrom(row_table_batch, num_rows_batch,
                /*source_row_ids=*/NULLPTR));
                }
                // The row table must contain an offset buffer.ASSERT_NE(row_table_right.offsets(), NULLPTR);
                ASSERT_EQ(row_table_right.length(), num_rows_row_table);
                }
                // The rows to compare: all rows in the batch to the last num_rows_batch rows of the// row table.
                std::vector<uint32_t> row_ids_to_compare(num_rows_batch);
                std::iota(row_ids_to_compare.begin(), row_ids_to_compare.end(),
                num_rows_row_table - num_rows_batch);
                TempVectorStack stack;
                ASSERT_OK(
                stack.Init(pool, KeyCompare::CompareColumnsToRowsTempStackUsage(num_rows_batch)));
                LightContext ctx{CpuInfo::GetInstance()->hardware_flags(), &stack};
                {
                // No selection, output no match row ids.uint32_t num_rows_no_match;
                std::vector<uint16_t> row_ids_out(num_rows_batch);
                KeyCompare::CompareColumnsToRows(num_rows_batch, /*sel_left_maybe_null=*/NULLPTR,
                row_ids_to_compare.data(), &ctx, &num_rows_no_match,
                row_ids_out.data(), columns_left, row_table_right,
                /*are_cols_in_encoding_order=*/true,
                /*out_match_bitvector_maybe_null=*/NULLPTR);
                ASSERT_EQ(num_rows_no_match, 0);
                }
                {
                // No selection, output match bit vector.
                std::vector<uint8_t> match_bitvector(BytesForBits(num_rows_batch));
                KeyCompare::CompareColumnsToRows(
                num_rows_batch, /*sel_left_maybe_null=*/NULLPTR, row_ids_to_compare.data(), &ctx,
                /*out_num_rows=*/NULLPTR, /*out_sel_left_maybe_same=*/NULLPTR, columns_left,
                row_table_right,
                /*are_cols_in_encoding_order=*/true, match_bitvector.data());
                ASSERT_EQ(arrow::internal::CountSetBits(match_bitvector.data(), 0, num_rows_batch),
                num_rows_batch);
                }
                std::vector<uint16_t> selection_left(num_rows_batch);
                std::iota(selection_left.begin(), selection_left.end(), 0);
                {
                // With selection, output no match row ids.uint32_t num_rows_no_match;
                std::vector<uint16_t> row_ids_out(num_rows_batch);
                KeyCompare::CompareColumnsToRows(num_rows_batch, selection_left.data(),
                row_ids_to_compare.data(), &ctx, &num_rows_no_match,
                row_ids_out.data(), columns_left, row_table_right,
                /*are_cols_in_encoding_order=*/true,
                /*out_match_bitvector_maybe_null=*/NULLPTR);
                ASSERT_EQ(num_rows_no_match, 0);
                }
                {
                // With selection, output match bit vector.
                std::vector<uint8_t> match_bitvector(BytesForBits(num_rows_batch));
                KeyCompare::CompareColumnsToRows(
                num_rows_batch, selection_left.data(), row_ids_to_compare.data(), &ctx,
                /*out_num_rows=*/NULLPTR, /*out_sel_left_maybe_same=*/NULLPTR, columns_left,
                row_table_right,
                /*are_cols_in_encoding_order=*/true, match_bitvector.data());
                ASSERT_EQ(arrow::internal::CountSetBits(match_bitvector.data(), 0, num_rows_batch),
                num_rows_batch);
                }
                }

                Component(s)

                C++

                Metadata

                Metadata

                Assignees

                Type

                No type

                Projects

                No projects

                  Milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions