[BUG] libdvbv5: dvbv5-scan hangs forever on a NIT with two versions in the stream

Vasco Valadares Semana <[email protected]>
Newsgroups org.kernel.vger.linux-media
Message-ID <CADybn6f2E3Vqus5fxcPN-bKp7Em-jyzo1ph_AXRrW=c4wUe_3A@mail.gmail.com>
Hello,

dvbv5-scan hangs indefinitely on one of my DVB-S2 transponders. I can
reproduce it every time and I have collected traces and a stream capture.

I am a developer but new to this codebase, so the source reading in the
second half is offered as a possible lead rather than a diagnosis. I used
an AI assistant to help interpret the captures and find my way around the
code, so it is worth checking the reasoning rather than taking it at face
value. The observations in the first half are mine and reproducible.

Environment:

  dvbv5-scan 1.22.1 (Debian bookworm, libdvbv5-0 1.22.1-5+b2)
  TBS ECP3 card, DVB-S2
  Turksat 42.0E, 12685 V (same on 12685 H and 12729 V)

The code I ended up looking at appears unchanged in current master
(95ad25f).


What I can confirm
==================

Reproducer, single mux, no -F, no -T:

  $ cat /tmp/one.conf
  [T]
  DELIVERY_SYSTEM = DVBS2
  FREQUENCY = 12685000
  POLARIZATION = VERTICAL
  SYMBOL_RATE = 30000000
  INNER_FEC = 2/3
  INVERSION = AUTO
  MODULATION = PSK/8
  PILOT = AUTO
  ROLLOFF = 35
  STREAM_ID = 0

  $ dvbv5-scan -a 1 -I DVBV5 -l EXTENDED -o /tmp/out.conf /tmp/one.conf
  Using LNBf EXTENDED
  [...]
  Scanning frequency #1 12685000
  Lock   (0x1f) C/N= 12,20dB
  <never returns, killed after 5 minutes>

Same with -l UNIVERSAL. The process sits in do_select on
/dev/dvb/adapter1/demux0. strace shows the same 102 byte section being
read every ~8 seconds, with the 10 second select window restarting each
time:

  12:36:56.853097 read(4, "@\360c\4.\325\1\1\360\n@\10\5TURKSAT"...,
      4096) = 102
  12:36:56.853348 pselect6(1024, [4], NULL, NULL, {tv_sec=10, tv_nsec=0},
      NULL) = 1 (in [4], left {tv_sec=1, tv_nsec=984464352})
  12:37:04.869171 read(4, "@\360c\4.\325\1\1\360\n@\10\5TURKSAT"...,
      4096) = 102
  12:37:04.869306 pselect6(1024, [4], NULL, NULL, {tv_sec=10, tv_nsec=0},
      NULL) = 1 (in [4], left {tv_sec=2, tv_nsec=808841})
  12:37:12.868722 read(4, "@\360c\4.\325\1\1\360\n@\10\5TURKSAT"...,
      4096) = 102
  [repeats indefinitely]

  (lines folded for mail, they are single lines in the original)

The mux itself looks healthy otherwise: it locks, carries about 55 Mbps,
and PAT, SDT and the PMTs all parse fine. Other transponders on the same
dish and adapter scan normally, so I do not think this is a reception or
configuration problem on my side.

Decoding PID 0x10 from a 15 second raw TS capture (dvbv5-zap -P -o) gives
three distinct NIT section headers:

  table_id=0x40 net_id=1070 ver=5   section=0/0
  table_id=0x40 net_id=1070 ver=10  section=0/1
  table_id=0x40 net_id=1070 ver=10  section=1/1

Version 5 announces a single section table, version 10 announces two, and
both are broadcast in a loop. My understanding is that this is not
allowed, so the stream is at fault to begin with. Still, it seems like
something the scanner should survive.

For comparison, a transponder that scans fine (12458 V, same dish and
adapter) shows a single NIT, version 10, section 0/0.


What I am less sure about
=========================

Reading dvb-scan.c, struct dvb_table_filter_ext_priv keys extensions on
ext_id and does not appear to carry a version number:

  struct dvb_table_filter_ext_priv {
          int last_section;
          unsigned long is_read_bits[BITS_TO_LONGS(256)];
          int ext_id;
          int first_section;
          int done;
  };

If I am reading it right, sections from both versions would then share one
extension, and last_section is only assigned when the extension is created
(dvb-scan.c:229 and :256) rather than revised afterwards. That would leave
the completion test

  is_all_bits_set(ext->last_section, ext->is_read_bits)

comparing against whichever section count arrived first. I could not work
out an arrival order for this stream that satisfies it, but I did not step
through it under a debugger, so I may simply be missing the path. Repeated
sections seem to take

  if (test_bit(h.section_id, ext->is_read_bits))
          return 0;

which would keep dvb_parse_section() returning 0.

Separately, in dvb_read_sections():

  do {
          do {
                  available = dvb_poll(parms, dmx_fd, timeout);
          } while (available < 0 && errno == EOVERFLOW);
          /* ... */
          if (available <= 0) {
                  dvb_logerr(_("%s: no data read on section filter"),
                             __func__);
                  ret = -1;
                  break;
          }
          /* ... */
          ret = dvb_parse_section(parms, sect, buf, buf_length);
  } while (!ret);

timeout looks like it bounds each dvb_poll() rather than the loop as a
whole, so an arriving section resets it. That would match the strace,
where the available <= 0 exit is never reached because data keeps coming.


What might help
===============

If version_number were tracked per extension and collection restarted when
it changed, I would expect this stream to settle on one version and
complete. And a bound on total time in dvb_read_sections() would stop any
table that cannot be assembled from hanging the caller outright.

I have not sent a patch. I would probably get the details wrong, and I do
not know whether restarting on a version change causes problems for the
gap tolerant paths (EIT, ts_id != -1).

I can test patches against this transponder, it reproduces on demand, at
least for now. I also still have the raw capture if a filtered PID 0x10
extract would be useful.

Thanks,
Vasco Valadares Semana
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.