@@ -578,6 +578,55 @@ dst_hexdump(pgp_dest_t &dst, const std::vector<uint8_t> &data)
578578namespace rnp {
579579using namespace pgp ;
580580
581+ /* Source wrapper which limits the number of bytes which may be read through it,
582+ * used on top of the decompressed packet contents during the dump. */
583+ typedef struct dump_limited_src_param_t {
584+ pgp_source_t * readsrc;
585+ std::shared_ptr<dump_budget_t > budget;
586+ } dump_limited_src_param_t ;
587+
588+ static bool
589+ dump_limited_src_read (pgp_source_t *src, void *buf, size_t len, size_t *readres)
590+ {
591+ auto param = static_cast <dump_limited_src_param_t *>(src->param );
592+ if (!param->budget ->left ) {
593+ if (!param->budget ->hit ) {
594+ RNP_LOG (" too much decompressed data during the dump, stopping." );
595+ param->budget ->hit = true ;
596+ }
597+ return false ;
598+ }
599+ len = std::min (len, param->budget ->left );
600+ if (!param->readsrc ->read (buf, len, readres)) {
601+ return false ;
602+ }
603+ param->budget ->left -= *readres;
604+ return true ;
605+ }
606+
607+ static void
608+ dump_limited_src_close (pgp_source_t *src)
609+ {
610+ free (src->param );
611+ }
612+
613+ static rnp_result_t
614+ init_dump_limited_src (pgp_source_t * src,
615+ pgp_source_t * readsrc,
616+ const std::shared_ptr<dump_budget_t > &budget)
617+ {
618+ if (!init_src_common (src, sizeof (dump_limited_src_param_t ))) {
619+ return RNP_ERROR_OUT_OF_MEMORY ; // LCOV_EXCL_LINE
620+ }
621+ auto param = static_cast <dump_limited_src_param_t *>(src->param );
622+ param->readsrc = readsrc;
623+ param->budget = budget;
624+ src->raw_read = dump_limited_src_read;
625+ src->raw_close = dump_limited_src_close;
626+ src->type = PGP_STREAM_PARLEN_PACKET ;
627+ return RNP_SUCCESS ;
628+ }
629+
581630void
582631DumpContext::copy_params (const DumpContext &ctx)
583632{
@@ -588,6 +637,8 @@ DumpContext::copy_params(const DumpContext &ctx)
588637 layers = ctx.layers ;
589638 stream_pkts = ctx.stream_pkts ;
590639 failures = ctx.failures ;
640+ dumped_pkts = ctx.dumped_pkts ;
641+ zbudget = ctx.zbudget ;
591642}
592643
593644bool
@@ -1442,6 +1493,10 @@ DumpContextDst::dump_compressed()
14421493 if (ret) {
14431494 return ret;
14441495 }
1496+ Source lsrc;
1497+ if ((ret = init_dump_limited_src (&lsrc.src (), &zsrc->src (), zbudget))) {
1498+ return ret;
1499+ }
14451500
14461501 dst_printf (dst, " Compressed data packet\n " );
14471502 indent_dest_increase (dst);
@@ -1451,10 +1506,15 @@ DumpContextDst::dump_compressed()
14511506 dst_print_zalg (dst, NULL , (pgp_compression_type_t ) zalg);
14521507 dst_printf (dst, " Decompressed contents:\n " );
14531508
1454- std::unique_ptr<DumpContextDst> ctx (new DumpContextDst (zsrc-> src (), dst));
1509+ std::unique_ptr<DumpContextDst> ctx (new DumpContextDst (lsrc. src (), dst));
14551510 ctx->copy_params (*this );
14561511 ret = ctx->dump (true );
14571512 copy_params (*ctx);
1513+ if (ret && zbudget->hit ) {
1514+ /* limit on the decompressed data was reached - dump what we have so far */
1515+ dst_printf (dst, " :too much decompressed data, stopping.\n " );
1516+ ret = RNP_SUCCESS ;
1517+ }
14581518 indent_dest_decrease (dst);
14591519 return ret;
14601520}
@@ -1521,6 +1581,16 @@ DumpContextDst::dump_raw_packets()
15211581 }
15221582
15231583 while (!src.eof ()) {
1584+ if (zbudget->hit ) {
1585+ dst_printf (dst, " :too much decompressed data, stopping.\n " );
1586+ return RNP_SUCCESS ;
1587+ }
1588+ if (++dumped_pkts > MAXIMUM_DUMP_PKTS ) {
1589+ RNP_LOG (" Too many packets during the dump." );
1590+ dst_printf (dst, " :too many packets, stopping.\n " );
1591+ return RNP_SUCCESS ;
1592+ }
1593+
15241594 pgp_packet_hdr_t hdr{};
15251595 size_t off = src.readb ;
15261596 rnp_result_t hdrret = stream_peek_packet_hdr (&src, &hdr);
@@ -1672,7 +1742,17 @@ DumpContextDst::dump(bool raw_only)
16721742 DumpContextDst ctx (armor.src (), dst);
16731743 ctx.copy_params (*this );
16741744 ret = ctx.dump (true );
1675- if (ret) {
1745+ if (ret && !zbudget->hit ) {
1746+ break ;
1747+ }
1748+ /* accumulate counters, but not layers: each armored message is
1749+ * dumped at the same nesting level */
1750+ stream_pkts = ctx.stream_pkts ;
1751+ failures = ctx.failures ;
1752+ dumped_pkts = ctx.dumped_pkts ;
1753+ if (zbudget->hit ) {
1754+ dst_printf (dst, " :too much decompressed data, stopping.\n " );
1755+ ret = RNP_SUCCESS ;
16761756 break ;
16771757 }
16781758 }
@@ -2520,6 +2600,10 @@ DumpContextJson::dump_compressed(nlohmann::ordered_json &pkt)
25202600 if (ret) {
25212601 return ret;
25222602 }
2603+ Source lsrc;
2604+ if ((ret = init_dump_limited_src (&lsrc.src (), &zsrc->src (), zbudget))) {
2605+ return ret;
2606+ }
25232607
25242608 uint8_t zalg;
25252609 get_compressed_src_alg (&zsrc->src (), &zalg);
@@ -2528,10 +2612,15 @@ DumpContextJson::dump_compressed(nlohmann::ordered_json &pkt)
25282612 }
25292613
25302614 nlohmann::ordered_json contents;
2531- DumpContextJson ctx (zsrc-> src (), &contents);
2615+ DumpContextJson ctx (lsrc. src (), &contents);
25322616 ctx.copy_params (*this );
25332617 ret = ctx.dump (true );
25342618 copy_params (ctx);
2619+ if (zbudget->hit ) {
2620+ /* limit on the decompressed data was reached - dump what we have so far */
2621+ pkt[" contents" ] = std::move (contents);
2622+ return RNP_SUCCESS ;
2623+ }
25352624 if (!ret) {
25362625 pkt[" contents" ] = std::move (contents);
25372626 }
@@ -2615,6 +2704,14 @@ DumpContextJson::dump_raw_packets()
26152704 }
26162705
26172706 while (!src.eof ()) {
2707+ if (zbudget->hit ) {
2708+ break ;
2709+ }
2710+ if (++dumped_pkts > MAXIMUM_DUMP_PKTS ) {
2711+ RNP_LOG (" Too many packets during the dump." );
2712+ break ;
2713+ }
2714+
26182715 auto & pkt = pkts.emplace_back (nlohmann::ordered_json::object ());
26192716 pgp_packet_hdr_t hdr{};
26202717 if (!dump_pkt_hdr (hdr, pkt)) {
@@ -2737,12 +2834,21 @@ DumpContextJson::dump(bool raw_only)
27372834 DumpContextJson ctx (armor.src (), &block);
27382835 ctx.copy_params (*this );
27392836 ret = ctx.dump (true );
2740- if (ret) {
2837+ if (ret && !zbudget-> hit ) {
27412838 break ;
27422839 }
2840+ /* accumulate counters, but not layers: each armored message is
2841+ * dumped at the same nesting level */
2842+ stream_pkts = ctx.stream_pkts ;
2843+ failures = ctx.failures ;
2844+ dumped_pkts = ctx.dumped_pkts ;
27432845 if (block.is_array ()) {
27442846 res.insert (res.end (), block.begin (), block.end ());
27452847 }
2848+ if (zbudget->hit ) {
2849+ ret = RNP_SUCCESS ;
2850+ break ;
2851+ }
27462852 }
27472853 *json = std::move (res);
27482854 return ret;
0 commit comments