[libdvdnav-devel] [Git][videolan/libdvdread][master] 3 commits: dvd_reader: check arguments before dereferencing dvd_file

Jean-Baptiste Kempf (@jbk) gitlab at videolan.org
Mon Aug 3 19:41:31 UTC 2026



Jean-Baptiste Kempf pushed to branch master at VideoLAN / libdvdread


Commits:
f5cdeae9 by Jean-Baptiste Kempf at 2026-07-31T21:22:31+02:00
dvd_reader: check arguments before dereferencing dvd_file

- - - - -
5ad3046f by Jean-Baptiste Kempf at 2026-07-31T21:22:38+02:00
dvd_reader: set the stream type before requesting the title key

On CPXM-enabled libdvdcss, css_title() only issues a CSS SEEK_KEY when
the device's stream type is DVD_V; for DVD_A/DVD_VR it degrades to a
plain seek. The per-file stream type was set only after the title key
was requested, so on a hybrid DVD-Audio disc a borrowed video title set
read right after an AOB had its key request run while the device was
still in DVD_A mode: the key was never cracked and the video decrypted
to garbage. In DVDOpenVOBUDF the same ordering made initAllCSSKeys() a
no-op (and css_state was then latched to 2, so it never retried).

Move dvdinput_set_stream() ahead of the dvdinput_title()/initAllCSSKeys()
calls in DVDReadBlocks and DVDOpenVOBUDF, and ahead of the per-device
dvdinput_title() in the DVDOpenVOBPath menu branch.

- - - - -
c8bf8719 by Jean-Baptiste Kempf at 2026-07-31T21:23:40+02:00
dvd_reader: cache the audio-to-video title set link

DVDAudioLinkedVTS reopens and reparses the ATS IFO and the whole AMG IFO
with its track table on every call. It is called from both DVDFileStat
and DVDOpenFile, so the usual stat-then-open sequence resolves the same
link twice per title, and during a title enumeration every failed probe
of a nonexistent title pays two IFO open attempts. The link is a fixed
property of the disc, so memoize it per audio title set: keep the
resolver as DVDAudioResolveLinkedVTS and add a caching front.

- - - - -


1 changed file:

- src/dvd_reader.c


Changes:

=====================================
src/dvd_reader.c
=====================================
@@ -63,6 +63,9 @@
 
 #define DEFAULT_UDF_CACHE_LEVEL 1
 
+/* DVD-Audio allows up to 99 audio title sets. */
+#define AUDIO_LINKED_VTS_MAX 100
+
 struct dvd_reader_device_s {
   /* Basic information. */
   int isImageFile;
@@ -81,6 +84,11 @@ struct dvd_reader_device_s {
   /* Filesystem cache */
   int udfcache_level; /* 0 - turned off, 1 - on */
   void *udfcache;
+
+  /* Cache of the video title set each audio title set borrows on a hybrid
+   * disc, indexed by ATS number: 0 = not resolved yet, -1 = no link,
+   * > 0 = the linked VTS number. See DVDAudioLinkedVTS(). */
+  int audio_linked_vts[ AUDIO_LINKED_VTS_MAX ];
 };
 
 #define TITLES_MAX 9
@@ -1160,12 +1168,15 @@ static dvd_file_t *DVDOpenVOBUDF( dvd_reader_t *ctx, int title, int menu,
     }
   }
 
+  /* Set the stream type before cracking keys: initAllCSSKeys() requests the
+   * title keys on this same device and css_title() keys off its stream type. */
+  dvdinput_set_stream( ctx->rd->dev, stream_type );
+
   if( stream_type == DVD_V && ctx->rd->css_state == 1 /* Need key init */ ) {
     initAllCSSKeys( ctx );
     ctx->rd->css_state = 2;
   }
 
-  dvdinput_set_stream( ctx->rd->dev, stream_type );
   return dvd_file;
 }
 
@@ -1230,9 +1241,9 @@ static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *ctx, int title, int menu,
     }
     dvd_file->title_sizes[ 0 ] = BYTES_TO_DVD_BLOCKS_CEIL(fileinfo.size);
     dvd_file->title_devs[ 0 ] = dev;
+    dvdinput_set_stream( dev, stream_type );
     dvdinput_title( dvd_file->title_devs[0], 0);
     dvd_file->filesize = dvd_file->title_sizes[ 0 ];
-    dvdinput_set_stream( dev, stream_type );
 
   } else {
 
@@ -1317,7 +1328,7 @@ static dvd_file_t *DVDOpenVOBPath( dvd_reader_t *ctx, int title, int menu,
  * objects of its own and instead borrow the title VOBs of a video title set.
  *
  * Returns the linked VTS number, or 0 when the ATS links to no video title set. */
-static int DVDAudioLinkedVTS( dvd_reader_t *ctx, int titlenum )
+static int DVDAudioResolveLinkedVTS( dvd_reader_t *ctx, int titlenum )
 {
   ifo_handle_t *ifo;
   uint32_t vts_sa;
@@ -1355,6 +1366,28 @@ static int DVDAudioLinkedVTS( dvd_reader_t *ctx, int titlenum )
   return vtsn;
 }
 
+/* The link is a fixed property of the disc, but both DVDOpenFile and
+ * DVDFileStat resolve it per title, so an uncached lookup reopens and
+ * reparses the ATS and AMG IFOs on every stat/open call and on every failed
+ * probe of a nonexistent title. Memoize it per audio title set. */
+static int DVDAudioLinkedVTS( dvd_reader_t *ctx, int titlenum )
+{
+  int *cached = NULL;
+  int vtsn;
+
+  if( titlenum >= 0 && titlenum < AUDIO_LINKED_VTS_MAX ) {
+    cached = &ctx->rd->audio_linked_vts[ titlenum ];
+    if( *cached != 0 )
+      return *cached > 0 ? *cached : 0;
+  }
+
+  vtsn = DVDAudioResolveLinkedVTS( ctx, titlenum );
+
+  if( cached )
+    *cached = vtsn > 0 ? vtsn : -1;
+  return vtsn;
+}
+
 dvd_file_t *DVDOpenFile( dvd_reader_t *ctx, int titlenum,
                          dvd_read_domain_t domain )
 {
@@ -1849,14 +1882,24 @@ static int DVDReadBlocksPath( const dvd_file_t *dvd_file, unsigned int offset,
 ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset,
                        size_t block_count, unsigned char *data )
 {
-  dvd_reader_t *ctx = dvd_file->ctx;
-  dvd_reader_device_t *dvd = ctx->rd;
+  dvd_reader_t *ctx;
+  dvd_reader_device_t *dvd;
   int ret;
 
   /* Check arguments. */
   if( dvd_file == NULL || offset < 0 || data == NULL )
     return -1;
 
+  ctx = dvd_file->ctx;
+  dvd = ctx->rd;
+
+  /* The decryption scheme follows the file, so it must be set before the
+   * title key is requested below: css_title() decides whether to crack a
+   * CSS key from the device's current stream type, and on a hybrid disc the
+   * previous read may have left a different scheme selected. */
+  if( dvd->isImageFile )
+    dvdinput_set_stream( dvd->dev, dvd_file->stream_type );
+
   /* Hack, and it will still fail for multiple opens in a threaded app ! */
   if( dvd->css_title != dvd_file->css_title ) {
       dvd->css_title = dvd_file->css_title;
@@ -1870,7 +1913,6 @@ ssize_t DVDReadBlocks( dvd_file_t *dvd_file, int offset,
   }
 
   if( dvd->isImageFile ) {
-    dvdinput_set_stream( dvd->dev, dvd_file->stream_type );
     ret = DVDReadBlocksUDF( dvd_file, (uint32_t)offset,
                             block_count, data, DVDINPUT_READ_DECRYPT );
   } else {



View it on GitLab: https://code.videolan.org/videolan/libdvdread/-/compare/c32b0dd167b37d88b3427bab010b676982323c48...c8bf871908eb21d2a81049042cb576e224a24a0e

-- 
View it on GitLab: https://code.videolan.org/videolan/libdvdread/-/compare/c32b0dd167b37d88b3427bab010b676982323c48...c8bf871908eb21d2a81049042cb576e224a24a0e
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help




More information about the libdvdnav-devel mailing list