@@ -2521,6 +2521,102 @@ TEST_F(MB_29287, dataloss_hole) {
25212521 EXPECT_EQ (1 , vb->checkpointManager ->getNumOfCursors ());
25222522}
25232523
2524+ class XattrCompressedTest
2525+ : public SingleThreadedEPBucketTest,
2526+ public ::testing::WithParamInterface<::testing::tuple<bool , bool >> {
2527+ public:
2528+ bool isXattrSystem () const {
2529+ return ::testing::get<0 >(GetParam ());
2530+ }
2531+ bool isSnappy () const {
2532+ return ::testing::get<1 >(GetParam ());
2533+ }
2534+ };
2535+
2536+ // Create a replica VB and consumer, then send it an xattr value which should
2537+ // of been stripped at the source, but wasn't because of MB29040. Then check
2538+ // the consumer sanitises the document. Run the test with user/system xattrs
2539+ // and snappy on/off
2540+ TEST_P (XattrCompressedTest, MB_29040_sanitise_input) {
2541+ setVBucketStateAndRunPersistTask (vbid, vbucket_state_replica);
2542+
2543+ auto consumer = std::make_shared<MockDcpConsumer>(
2544+ *engine, cookie, " MB_29040_sanitise_input" );
2545+ int opaque = 1 ;
2546+ ASSERT_EQ (ENGINE_SUCCESS, consumer->addStream (opaque, vbid, /* flags*/ 0 ));
2547+
2548+ std::string body;
2549+ if (!isXattrSystem ()) {
2550+ body.assign (" value" );
2551+ }
2552+ auto value = createXattrValue (body, isXattrSystem (), isSnappy ());
2553+
2554+ // Send deletion in a single seqno snapshot
2555+ int64_t bySeqno = 1 ;
2556+ EXPECT_EQ (ENGINE_SUCCESS,
2557+ consumer->snapshotMarker (
2558+ opaque, vbid, bySeqno, bySeqno, MARKER_FLAG_CHK));
2559+
2560+ cb::const_byte_buffer valueBuf{
2561+ reinterpret_cast <const uint8_t *>(value.data ()), value.size ()};
2562+ EXPECT_EQ (
2563+ ENGINE_SUCCESS,
2564+ consumer->deletion (
2565+ opaque,
2566+ {" key" , DocNamespace::DefaultCollection},
2567+ valueBuf,
2568+ /* priv_bytes*/ 0 ,
2569+ PROTOCOL_BINARY_DATATYPE_XATTR |
2570+ (isSnappy () ? PROTOCOL_BINARY_DATATYPE_SNAPPY : 0 ),
2571+ /* cas*/ 3 ,
2572+ vbid,
2573+ bySeqno,
2574+ /* revSeqno*/ 0 ,
2575+ /* meta*/ {}));
2576+
2577+ EXPECT_EQ (std::make_pair (false , size_t (1 )),
2578+ getEPBucket ().flushVBucket (vbid));
2579+
2580+ ASSERT_EQ (ENGINE_SUCCESS, consumer->closeStream (opaque, vbid));
2581+
2582+ // Switch to active
2583+ setVBucketStateAndRunPersistTask (vbid, vbucket_state_active);
2584+
2585+ get_options_t options = static_cast <get_options_t >(
2586+ QUEUE_BG_FETCH | HONOR_STATES | TRACK_REFERENCE | DELETE_TEMP |
2587+ HIDE_LOCKED_CAS | TRACK_STATISTICS | GET_DELETED_VALUE);
2588+ auto gv = store->get (
2589+ {" key" , DocNamespace::DefaultCollection}, vbid, cookie, options);
2590+ EXPECT_EQ (ENGINE_EWOULDBLOCK, gv.getStatus ());
2591+
2592+ // Manually run the bgfetch task.
2593+ MockGlobalTask mockTask (engine->getTaskable (), TaskId::MultiBGFetcherTask);
2594+ store->getVBucket (vbid)->getShard ()->getBgFetcher ()->run (&mockTask);
2595+ gv = store->get ({" key" , DocNamespace::DefaultCollection},
2596+ vbid,
2597+ cookie,
2598+ GET_DELETED_VALUE);
2599+ ASSERT_EQ (ENGINE_SUCCESS, gv.getStatus ());
2600+
2601+ // This is the only system key test_helpers::createXattrValue gives us
2602+ cb::xattr::Blob blob;
2603+ blob.set (" _sync" , " {\" cas\" :\" 0xdeadbeefcafefeed\" }" );
2604+
2605+ EXPECT_TRUE (gv.item ->isDeleted ());
2606+ EXPECT_EQ (0 , gv.item ->getFlags ());
2607+ EXPECT_EQ (3 , gv.item ->getCas ());
2608+ EXPECT_EQ (isXattrSystem () ? blob.size () : 0 ,
2609+ gv.item ->getValue ()->valueSize ());
2610+ EXPECT_EQ (isXattrSystem () ? PROTOCOL_BINARY_DATATYPE_XATTR
2611+ : PROTOCOL_BINARY_RAW_BYTES,
2612+ gv.item ->getDataType ());
2613+ }
2614+
25242615INSTANTIATE_TEST_CASE_P (XattrSystemUserTest,
25252616 XattrSystemUserTest,
25262617 ::testing::Bool (), );
2618+
2619+ INSTANTIATE_TEST_CASE_P (XattrCompressedTest,
2620+ XattrCompressedTest,
2621+ ::testing::Combine (::testing::Bool(),
2622+ ::testing::Bool()), );
0 commit comments