[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2025-6170: Stack-buffer-overflow in libxml2 2.13.8 xmllint interactive shell: the xmlShell() function in debugXML.c copies user input into fixed-size stack-allocated character arrays without bounding the copy loops by the array capacity, allowing crafted input to overwrite memory adjacent to the shell processing buffers.

# Vulnerable source (debugXML.c)
```c
    if ((node == NULL) || (buffer == NULL))
        return (-1);

    path = xmlGetNodePath(node);
    if (path == NULL)
	return (-1);

    /*
     * This test prevents buffer overflow, because this routine
     * is only called by xmlShell, in which the second argument is
     * 500 chars long.
     * It is a dirty hack before a cleaner solution is found.
     * Documentation should mention that the second argument must
     * be at least 500 chars long, and could be stripped if too long.
     */
    snprintf(buffer, 499, "%s", path);
    buffer[499] = '0';
    xmlFree(path);

    return (0);
}

/**
 * xmlShell:
 * @doc:  the initial document
 * @filename:  the output buffer
 * @input:  the line reading function
 * @output:  the output FILE*, defaults to stdout if NULL
 *
 * Implements the XML shell
 * This allow to load, validate, view, modify and save a document
 * using a environment similar to a UNIX commandline.
 */
void
xmlShell(xmlDocPtr doc, const char *filename, xmlShellReadlineFunc input,
         FILE * output)
{
    char prompt[500] = "/ > ";
    char *cmdline = NULL, *cur;
    char command[100];
    char arg[400];
    int i;
    xmlShellCtxtPtr ctxt;
    xmlXPathObjectPtr list;

    if (doc == NULL)
        return;
    if (filename == NULL)
        return;
    if (input == NULL)
        return;
    if (output == NULL)
        output = stdout;
    ctxt = (xmlShellCtxtPtr) xmlMalloc(sizeof(xmlShellCtxt));
    if (ctxt == NULL)
        /* ... */
                     (ctxt->node->ns->prefix), ctxt->node->name);
        else if ((ctxt->node != NULL) && (ctxt->node->name))
            snprintf(prompt, sizeof(prompt), "%s > ", ctxt->node->name);
        else
            snprintf(prompt, sizeof(prompt), "? > ");
        prompt[sizeof(prompt) - 1] = 0;

        /*
         * Get a new command line
         */
        cmdline = ctxt->input(prompt);
        if (cmdline == NULL)
            break;

        /*
         * Parse the command itself
         */
        cur = cmdline;
        while ((*cur == ' ') || (*cur == '\t'))
            cur++;
        i = 0;
        while ((*cur != ' ') && (*cur != '\t') &&
               (*cur != '\n') && (*cur != '\r')) {
            if (*cur == 0)
                break;
            command[i++] = *cur++;
        }
        command[i] = 0;
        if (i == 0)
            continue;

        /*
         * Parse the argument
         */
        while ((*cur == ' ') || (*cur == '\t'))
            cur++;
        i = 0;
        while ((*cur != '\n') && (*cur != '\r') && (*cur != 0)) {
            if (*cur == 0)
                break;
            arg[i++] = *cur++;
        }
        arg[i] = 0;

        /*
         * start interpreting the command
         */
        if (!strcmp(command, "exit"))
            break;
        if (!strcmp(command, "quit"))
            break;
        if (!strcmp(command, "bye"))
            break;
		if (!strcmp(command, "help")) {
		  fprintf(ctxt->output, "\tbase         display XML base of the node\n");
```

[Assistant]
diff --git a/debugXML.c b/debugXML.c
--- a/debugXML.c
+++ b/debugXML.c
@@ -2780,6 +2780,10 @@ xmlShellPwd(xmlShellCtxtPtr ctxt ATTRIBUTE_UNUSED, char *buffer,
     return (0);
 }

+#define MAX_PROMPT_SIZE     500
+#define MAX_ARG_SIZE        400
+#define MAX_COMMAND_SIZE    100
+
 /**
  * xmlShell:
  * @doc:  the initial document
@@ -2795,10 +2799,10 @@ void
 xmlShell(xmlDocPtr doc, const char *filename, xmlShellReadlineFunc input,
          FILE * output)
 {
-    char prompt[500] = "/ > ";
+    char prompt[MAX_PROMPT_SIZE] = "/ > ";
     char *cmdline = NULL, *cur;
-    char command[100];
-    char arg[400];
+    char command[MAX_COMMAND_SIZE];
+    char arg[MAX_ARG_SIZE];
     int i;
     xmlShellCtxtPtr ctxt;
     xmlXPathObjectPtr list;
@@ -2856,7 +2860,8 @@ xmlShell(xmlDocPtr doc, const char *filename, xmlShellReadlineFunc input,
             cur++;
         i = 0;
         while ((*cur != ' ') && (*cur != '\t') &&
-               (*cur != '\n') && (*cur != '\r')) {
+               (*cur != '\n') && (*cur != '\r') &&
+               (i < (MAX_COMMAND_SIZE - 1))) {
             if (*cur == 0)
                 break;
             command[i++] = *cur++;
@@ -2871,7 +2876,7 @@ xmlShell(xmlDocPtr doc, const char *filename, xmlShellReadlineFunc input,
         while ((*cur == ' ') || (*cur == '\t'))
             cur++;
         i = 0;
-        while ((*cur != '\n') && (*cur != '\r') && (*cur != 0)) {
+        while ((*cur != '\n') && (*cur != '\r') && (*cur != 0) && (i < (MAX_ARG_SIZE-1))) {
             if (*cur == 0)
                 break;
             arg[i++] = *cur++;
diff --git a/xmllint.c b/xmllint.c
--- a/xmllint.c
+++ b/xmllint.c
@@ -724,6 +724,9 @@ xmlHTMLValidityWarning(void *ctx, const char *msg, ...)
  ************************************************************************/
 #ifdef LIBXML_DEBUG_ENABLED
 #ifdef LIBXML_XPATH_ENABLED
+
+#define MAX_PROMPT_SIZE     500
+
 /**
  * xmlShellReadline:
  * @prompt:  the prompt value
@@ -754,9 +757,9 @@ xmlShellReadline(char *prompt) {
     if (prompt != NULL)
 	fprintf(stdout, "%s", prompt);
     fflush(stdout);
-    if (!fgets(line_read, 500, stdin))
+    if (!fgets(line_read, MAX_PROMPT_SIZE, stdin))
         return(NULL);
-    line_read[500] = 0;
+    line_read[MAX_PROMPT_SIZE] = 0;
     len = strlen(line_read);
     ret = (char *) malloc(len + 1);
     if (ret != NULL) {


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2025-64503: Out-of-bounds write (CWE-787) in libcupsfilters 2.1.1 cfFilterPDFToRaster() via the bundled PDF-to-raster filter. Reported as GHSA-893j-2wr2-wrh9.

# Vulnerable source (cupsfilters/pdftoraster.c)
```c
               "cfFilterPDFToRaster: cropbox = [ %f %f %f %f ]; rotate = %d",
               cropBox.x1, cropBox.x2, cropBox.y1, cropBox.y2,
               rotate);

  // Enter input page dimensions in header, so that if no page size got
  // specified for the job, the input size gets used via the header
  l = cropBox.x2 - cropBox.x1;
  if (l<0)
    l = -l;

  if (rotate == 90 || rotate == 270)
  doc->header.cupsPageSize[1] = l;
  else
  doc->header.cupsPageSize[0] = l;

  l = cropBox.y2 - cropBox.y1;
  if (l < 0)
    l = -l;
  if (rotate == 90 || rotate == 270)
    doc->header.cupsPageSize[0] = l;
  else
    doc->header.cupsPageSize[1] = l;
  if (rotate == 90 || rotate == 270)
  {
    doc->header.cupsImagingBBox[0] =
      doc->header.cupsPageSize[0] - cropBox.y1;
    doc->header.cupsImagingBBox[1] = cropBox.x2;
    doc->header.cupsImagingBBox[2] =
      doc->header.cupsPageSize[0] - cropBox.y2;
    doc->header.cupsImagingBBox[3] = cropBox.x1;
  }
 else
  {
    doc->header.cupsImagingBBox[0] = cropBox.x1;
    doc->header.cupsImagingBBox[1] =
      doc->header.cupsPageSize[1] - cropBox.y1;
    doc->header.cupsImagingBBox[2] = cropBox.x2;
    doc->header.cupsImagingBBox[3] =
      doc->header.cupsPageSize[1] - cropBox.y2;
  }
```

[Assistant]
diff --git a/cupsfilters/pdftoraster.c b/cupsfilters/pdftoraster.c
index fca1b48..56616c2 100644
--- a/cupsfilters/pdftoraster.c
+++ b/cupsfilters/pdftoraster.c
@@ -1814,6 +1814,20 @@ out_page(pdftoraster_doc_t *doc,
     doc->header.cupsPageSize[0] = l;
   else
     doc->header.cupsPageSize[1] = l;
+
+  //
+  // Maximum allowed page size for PDF is 200x200 inches (~ 5x5 m), or 14400x14400 pt
+  // https://community.adobe.com/t5/indesign-discussions/maximum-width-of-a-pdf/td-p/9217372
+  //
+  if (doc->header.cupsPageSize[0] > 14400) {
+    fprintf(stderr, "ERROR: Page width is %.2fpt, too large, cropping to 14400pt\n", doc->header.cupsPageSize[0]);
+    doc->header.cupsPageSize[0] = 14400;
+  }
+  if (doc->header.cupsPageSize[1] > 14400) {
+    fprintf(stderr, "ERROR: Page height is %.2fpt, too large, cropping to 14400pt\n", doc->header.cupsPageSize[1]);
+    doc->header.cupsPageSize[1] = 14400;
+  }
+
   if (rotate == 90 || rotate == 270)
   {
     doc->header.cupsImagingBBox[0] =


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2025-69693: Out-of-bounds read in decode_slice() in FFmpeg's RV60 decoder. The quantization parameter is computed as the sum of a frame-level base value (range 0 to 63) and a per-slice signed offset, which can yield a value exceeding the valid range for downstream lookup tables. The decoder checks only the lower bound before passing the value to internal lookup tables that require inputs within 0 to 63. A crafted RV60 bitstream can trigger this condition, resulting in an out-of-bounds read and potential crash or information disclosure.

# Vulnerable source (libavcodec/rv60dec.c)
```c
static int decode_slice(AVCodecContext *avctx, void *tdata, int cu_y, int threadnr)
{
    RV60Context *s = avctx->priv_data;
    AVFrame * frame = tdata;
    ThreadContext thread;
    GetBitContext gb;
    int qp, sel_qp, ret;

    thread.avg_data[0] = thread.avg_buffer;
    thread.avg_data[1] = thread.avg_buffer + 64*64;
    thread.avg_data[2] = thread.avg_buffer + 64*64 + 32*32;
    thread.avg_linesize[0] = 64;
    thread.avg_linesize[1] = 32;
    thread.avg_linesize[2] = 32;

    if ((ret = init_get_bits8(&gb, s->slice[cu_y].data, s->slice[cu_y].data_size)) < 0)
        return ret;

    for (int cu_x = 0; cu_x < s->cu_width; cu_x++) {
        if ((s->avctx->active_thread_type & FF_THREAD_SLICE) && cu_y)
            ff_thread_progress_await(&s->progress[cu_y - 1], cu_x + 2);

        qp = s->qp + read_qp_offset(&gb, s->qp_off_type);
        if (qp < 0) {
            ret = AVERROR_INVALIDDATA;
            break;
        }
        sel_qp = calc_sel_qp(s->osvquant, qp);

        memset(thread.coded_blk, 0, sizeof(thread.coded_blk));
        thread.cu_split_pos = 0;

        if ((ret = decode_cu_r(s, frame, &thread, &gb, cu_x << 6, cu_y << 6, 6, qp, sel_qp)) < 0)
            break;

        if (s->deblock) {
            thread.cu_split_pos = 0;
            deblock_cu_r(s, frame, &thread, cu_x << 6, cu_y << 6, 6, qp);
        }

        if (s->avctx->active_thread_type & FF_THREAD_SLICE)
            ff_thread_progress_report(&s->progress[cu_y], cu_x + 1);
    }

    if (s->avctx->active_thread_type & FF_THREAD_SLICE)
        ff_thread_progress_report(&s->progress[cu_y], INT_MAX);

    return ret;
}
```

[Assistant]
diff --git a/libavcodec/rv60dec.c b/libavcodec/rv60dec.c
--- a/libavcodec/rv60dec.c
+++ b/libavcodec/rv60dec.c
@@ -2265,7 +2265,7 @@ static int decode_slice(AVCodecContext *avctx, void *tdata, int cu_y, int thread
             ff_thread_progress_await(&s->progress[cu_y - 1], cu_x + 2);

         qp = s->qp + read_qp_offset(&gb, s->qp_off_type);
-        if (qp < 0) {
+        if (qp < 0 || qp >= 64) {
             ret = AVERROR_INVALIDDATA;
             break;
         }


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2025-7207: mruby's compiler codegen stage allocated a new scope object for each function scope but failed to initialize the register-count field to match the local-count field when constructing the scope. This left the register-count potentially smaller than the number of named parameters, which could cause the code generator to underallocate the register frame and corrupt adjacent memory when compiling functions with many arguments.

# Vulnerable source (mrbgems/mruby-compiler/core/codegen.c)
```c
        loop = loop->prev;
      }
      else if (loop->type == LOOP_RESCUE) {
        loop = loop->prev;
      }
      else{
        break;
      }
    }
    if (!loop) {
      raise_error(s, "unexpected break");
      return;
    }

    if (loop->type == LOOP_NORMAL) {
      int tmp;

      if (loop->reg >= 0) {
        if (tree) {
          gen_move(s, loop->reg, cursp(), 0);
        }
        else {
          genop_1(s, OP_LOADNIL, loop->reg);
        }
      }
      tmp = genjmp(s, OP_JMPUW, loop->pc2);
      loop->pc2 = tmp;
    }
    else {
      if (!tree) {
        genop_1(s, OP_LOADNIL, cursp());
      }
      gen_return(s, OP_BREAK, cursp());
    }
  }
}

static void
loop_pop(codegen_scope *s, int val)
{
  if (val) {
    genop_1(s, OP_LOADNIL, cursp());
  }
  dispatch_linked(s, s->loop->pc2);
  s->loop = s->loop->prev;
  if (val) push();
}

static int
catch_handler_new(codegen_scope *s)
{
  size_t newsize = sizeof(struct mrb_irep_catch_handler) * (s->irep->clen + 1);
  s->catch_table = (struct mrb_irep_catch_handler*)codegen_realloc(s, (void*)s->catch_table, newsize);
  return s->irep->clen++;
}

static void
catch_handler_set(codegen_scope *s, int ent, enum mrb_catch_type type, uint32_t begin, uint32_t end, uint32_t target)
{
  struct mrb_irep_catch_handler *e;

  mrb_assert(ent >= 0 && ent < s->irep->clen);

  e = &s->catch_table[ent];
  uint8_to_bin(type, &e->type);
  mrb_irep_catch_handler_pack(begin, e->begin);
  mrb_irep_catch_handler_pack(end, e->end);
  mrb_irep_catch_handler_pack(target, e->target);
}

static struct RProc*
generate_code(mrb_state *mrb, parser_state *p, int val)
{
  codegen_scope *scope = scope_new(mrb, 0, 0);
  struct mrb_jmpbuf *prev_jmp = mrb->jmp;
  struct mrb_jmpbuf jmpbuf;
  struct RProc *proc;

  mrb->jmp = &jmpbuf;

  scope->mrb = mrb;
  scope->parser = p;
  scope->filename_sym = p->filename_sym;
  scope->filename_index = p->current_filename_index;

  MRB_TRY(mrb->jmp) {
    /* prepare irep */
```

[Assistant]
diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c
index be0bf875af..9deaa7fec5 100644
--- a/mrbgems/mruby-compiler/core/codegen.c
+++ b/mrbgems/mruby-compiler/core/codegen.c
@@ -4012,7 +4012,7 @@ scope_new(mrb_state *mrb, codegen_scope *prev, node *nlv)

   s->lv = nlv;
   s->sp += node_len(nlv)+1;        /* add self */
-  s->nlocals = s->sp;
+  s->nlocals = s->nregs = s->sp;
   if (nlv) {
     mrb_sym *lv;
     node *n = nlv;


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2025-7424: libxslt 1.1.43 and earlier is vulnerable to CVE-2025-7424, a type confusion that can lead to heap corruption. The psvi memory field is shared between stylesheet and input data without proper type tracking, causing confusion during XSLT transformations when the library attempts to read data stored by one code path using assumptions from another. An attacker can exploit this by crafting a malicious XSL stylesheet to corrupt memory and potentially execute arbitrary code.

# Vulnerable source (libxslt/xsltutils.h)
```c

/*
 * Output, reuse libxml I/O buffers.
 */
XSLTPUBFUN int XSLTCALL
		xsltSaveResultTo		(xmlOutputBufferPtr buf,
						 xmlDocPtr result,
						 xsltStylesheetPtr style);
XSLTPUBFUN int XSLTCALL
		xsltSaveResultToFilename	(const char *URI,
						 xmlDocPtr result,
						 xsltStylesheetPtr style,
						 int compression);
XSLTPUBFUN int XSLTCALL
		xsltSaveResultToFile		(FILE *file,
						 xmlDocPtr result,
						 xsltStylesheetPtr style);
XSLTPUBFUN int XSLTCALL
		xsltSaveResultToFd		(int fd,
						 xmlDocPtr result,
						 xsltStylesheetPtr style);
XSLTPUBFUN int XSLTCALL
		xsltSaveResultToString          (xmlChar **doc_txt_ptr,
                                                 int * doc_txt_len,
                                                 xmlDocPtr result,
                                                 xsltStylesheetPtr style);

/*
 * XPath interface
 */
XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL
		xsltXPathCompile		(xsltStylesheetPtr style,
						 const xmlChar *str);
XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL
		xsltXPathCompileFlags		(xsltStylesheetPtr style,
						 const xmlChar *str,
						 int flags);

#ifdef IN_LIBXSLT
/** DOC_DISABLE */
#define XSLT_SOURCE_NODE_MASK       15u
#define XSLT_SOURCE_NODE_HAS_KEY    1u
#define XSLT_SOURCE_NODE_HAS_ID     2u
int
xsltGetSourceNodeFlags(xmlNodePtr node);
int
xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node,
                       int flags);
int
xsltClearSourceNodeFlags(xmlNodePtr node, int flags);
void **
xsltGetPSVIPtr(xmlNodePtr cur);
/** DOC_ENABLE */
#endif

#ifdef WITH_PROFILER
/*
 * Profiling.
 */
XSLTPUBFUN void XSLTCALL
		xsltSaveProfiling		(xsltTransformContextPtr ctxt,
						 FILE *output);
XSLTPUBFUN xmlDocPtr XSLTCALL
		xsltGetProfileInformation	(xsltTransformContextPtr ctxt);

XSLTPUBFUN long XSLTCALL
		xsltTimestamp			(void);
XSLTPUBFUN void XSLTCALL
		xsltCalibrateAdjust		(long delta);
#endif

/**
 * XSLT_TIMESTAMP_TICS_PER_SEC:
 *
 * Sampling precision for profiling
 */
#define XSLT_TIMESTAMP_TICS_PER_SEC 100000l

/*
 * Hooks for the debugger.
 */

typedef enum {
    XSLT_DEBUG_NONE = 0, /* no debugging allowed */
    XSLT_DEBUG_INIT,
    XSLT_DEBUG_STEP,
    XSLT_DEBUG_STEPOUT,
    XSLT_DEBUG_NEXT,
    XSLT_DEBUG_STOP,
    XSLT_DEBUG_CONT,
    XSLT_DEBUG_RUN,
    XSLT_DEBUG_RUN_RESTART,
    XSLT_DEBUG_QUIT
```

[Assistant]
diff --git a/libxslt/xsltutils.h b/libxslt/xsltutils.h
index 2514774b3f11a..1e753eebadd98 100644
--- a/libxslt/xsltutils.h
+++ b/libxslt/xsltutils.h
@@ -261,6 +261,10 @@ XSLTPUBFUN xmlXPathCompExprPtr XSLTCALL
 #define XSLT_SOURCE_NODE_MASK       15u
 #define XSLT_SOURCE_NODE_HAS_KEY    1u
 #define XSLT_SOURCE_NODE_HAS_ID     2u
+#define XSLT_SOURCE_NODE_SHIFT_16   12u
+#define XSLT_SOURCE_NODE_SHIFT_32   28u
+#define XSLT_SOURCE_NODE_VALUE_MASK ((1 << XSLT_SOURCE_NODE_SHIFT_32) - 1)
+#define XSLT_SOURCE_NODE_VALUE_MAX  XSLT_SOURCE_NODE_VALUE_MASK
 int
 xsltGetSourceNodeFlags(xmlNodePtr node);
 int
@@ -268,6 +272,10 @@ xsltSetSourceNodeFlags(xsltTransformContextPtr ctxt, xmlNodePtr node,
                        int flags);
 int
 xsltClearSourceNodeFlags(xmlNodePtr node, int flags);
+int
+xsltSetSourceNodeValue(xmlNodePtr node, int value);
+int
+xsltGetSourceNodeValue(xmlNodePtr node);
 void **
 xsltGetPSVIPtr(xmlNodePtr cur);
 /** DOC_ENABLE */


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-0989: Stack overflow via unbounded recursion when parsing a RelaxNG schema in libxml2 2.13.9 (CWE-674, uncontrolled recursion / denial of service). A crafted .rng schema whose definitions reference other sub-schemas in a deeply nested chain makes the parser recurse without any depth bound while resolving each referenced sub-grammar, exhausting the call stack. Triggered by schema input alone during validation setup; no network or special privileges are required.

# Vulnerable source (relaxng.c, include/libxml/relaxng.h)
```c
/* ==== file: relaxng.c ==== */
 *
 * Author: Daniel Veillard
 */

/**
 * TODO:
 * - add support for DTD compatibility spec
 *   http://www.oasis-open.org/committees/relax-ng/compatibility-20011203.html
 * - report better mem allocations pbms at runtime and abort immediately.
 */

#define IN_LIBXML
#include "libxml.h"

#ifdef LIBXML_RELAXNG_ENABLED

#include <string.h>
#include <stdio.h>
#include <stddef.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <libxml/parserInternals.h>
#include <libxml/hash.h>
#include <libxml/uri.h>

#include <libxml/relaxng.h>

#include <libxml/xmlautomata.h>
#include <libxml/xmlregexp.h>
#include <libxml/xmlschemastypes.h>

#include "private/error.h"
#include "private/regexp.h"
#include "private/string.h"
#include "private/threads.h"

/*
 * The Relax-NG namespace
 */
static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
    "http://relaxng.org/ns/structure/1.0";

#define IS_RELAXNG(node, typ)						\
   ((node != NULL) && (node->ns != NULL) &&				\
    (node->type == XML_ELEMENT_NODE) &&					\
    (xmlStrEqual(node->name, (const xmlChar *) typ)) &&		\
    (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))

#define MAX_ERROR 5

typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;

        /* ... */
    xmlRelaxNGDefinePtr *defTab;        /* pointer to the allocated definitions */

    const char *buffer;
    int size;

    /* the document stack */
    xmlRelaxNGDocumentPtr doc;  /* Current parsed external ref */
    int docNr;                  /* Depth of the parsing stack */
    int docMax;                 /* Max depth of the parsing stack */
    xmlRelaxNGDocumentPtr *docTab;      /* array of docs */

    /* the include stack */
    xmlRelaxNGIncludePtr inc;   /* Current parsed include */
    int incNr;                  /* Depth of the include parsing stack */
    int incMax;                 /* Max depth of the parsing stack */
    xmlRelaxNGIncludePtr *incTab;       /* array of incs */

    int idref;                  /* requires idref checking */

    /* used to compile content models */
    xmlAutomataPtr am;          /* the automata */
    xmlAutomataStatePtr state;  /* used to build the automata */

    int crng;			/* compact syntax and other flags */
    int freedoc;		/* need to free the document */

    xmlResourceLoader resourceLoader;
    void *resourceCtxt;
        /* ... */
int
xmlRelaxParserSetFlag(xmlRelaxNGParserCtxt *ctxt, int flags)
{
    if (ctxt == NULL) return(-1);
    if (flags & XML_RELAXNGP_FREE_DOC) {
        ctxt->crng |= XML_RELAXNGP_FREE_DOC;
	flags -= XML_RELAXNGP_FREE_DOC;
    }
    if (flags & XML_RELAXNGP_CRNG) {
        ctxt->crng |= XML_RELAXNGP_CRNG;
	flags -= XML_RELAXNGP_CRNG;
    }
    if (flags != 0) return(-1);
    return(0);
}

/************************************************************************
 *									*
 *			Document functions				*
 *									*
 ************************************************************************/
static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
                                      xmlDocPtr doc);

static xmlDoc *
xmlRelaxReadFile(xmlRelaxNGParserCtxtPtr ctxt, const char *filename) {
    xmlParserCtxtPtr pctxt;
    xmlDocPtr doc;
        /* ... */
    if (ctxt->serror != NULL)
        xmlCtxtSetErrorHandler(pctxt, ctxt->serror, ctxt->userData);
    if (ctxt->resourceLoader != NULL)
        xmlCtxtSetResourceLoader(pctxt, ctxt->resourceLoader,
                                 ctxt->resourceCtxt);
    doc = xmlCtxtReadMemory(pctxt, buf, size, NULL, NULL, 0);
    xmlFreeParserCtxt(pctxt);

    return(doc);
}

/**
 * Pushes a new include on top of the include stack
 *
 * @param ctxt  the parser context
 * @param value  the element doc
 * @returns 0 in case of error, the index in the stack otherwise
 */
static int
xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
                      xmlRelaxNGIncludePtr value)
{
    if (ctxt->incTab == NULL) {
        ctxt->incMax = 4;
        ctxt->incNr = 0;
        ctxt->incTab =
            (xmlRelaxNGIncludePtr *) xmlMalloc(ctxt->incMax *
                                               sizeof(ctxt->incTab[0]));
        if (ctxt->incTab == NULL) {
            xmlRngPErrMemory(ctxt);
            return (0);
        }
    }
    if (ctxt->incNr >= ctxt->incMax) {
        ctxt->incMax *= 2;
        ctxt->incTab =
            (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
                                                ctxt->incMax *
                                                sizeof(ctxt->incTab[0]));
        if (ctxt->incTab == NULL) {
            xmlRngPErrMemory(ctxt);
            return (0);
        }
    }
    ctxt->incTab[ctxt->incNr] = value;
    ctxt->inc = value;
    return (ctxt->incNr++);
}

/**
 * Pops the top include from the include stack
 *
 * @param ctxt  the parser context
        /* ... */

    /*
     * transmit the ns if needed
     */
    if (ns != NULL) {
        root = xmlDocGetRootElement(doc);
        if (root != NULL) {
            if (xmlHasProp(root, BAD_CAST "ns") == NULL) {
                xmlSetProp(root, BAD_CAST "ns", ns);
            }
        }
    }

    /*
     * push it on the stack
     */
    xmlRelaxNGIncludePush(ctxt, ret);

    /*
     * Some preprocessing of the document content, this include recursing
     * in the include stack.
     */

    doc = xmlRelaxNGCleanupDoc(ctxt, doc);
    if (doc == NULL) {
        ctxt->inc = NULL;
        return (NULL);
    }
        /* ... */

/**
 * parse a schema definition resource and build an internal
 * XML Schema structure which can be used to validate instances.
 *
 * @param ctxt  a Relax-NG parser context
 * @returns the internal XML RelaxNG structure built from the resource or
 *         NULL in case of error
 */
xmlRelaxNG *
xmlRelaxNGParse(xmlRelaxNGParserCtxt *ctxt)
{
    xmlRelaxNGPtr ret = NULL;
    xmlDocPtr doc;
    xmlNodePtr root;

    xmlRelaxNGInitTypes();

    if (ctxt == NULL)
        return (NULL);

    /*
     * First step is to parse the input document into an DOM/Infoset
     */
    if (ctxt->URL != NULL) {
        doc = xmlRelaxReadFile(ctxt, (const char *) ctxt->URL);
        if (doc == NULL) {
            xmlRngPErr(ctxt, NULL, XML_RNGP_PARSE_ERROR,

/* ==== file: include/libxml/relaxng.h ==== */

/*
 * Interfaces for parsing.
 */
XMLPUBFUN xmlRelaxNGParserCtxt *
		    xmlRelaxNGNewParserCtxt	(const char *URL);
XMLPUBFUN xmlRelaxNGParserCtxt *
		    xmlRelaxNGNewMemParserCtxt	(const char *buffer,
						 int size);
XMLPUBFUN xmlRelaxNGParserCtxt *
		    xmlRelaxNGNewDocParserCtxt	(xmlDoc *doc);

XMLPUBFUN int
		    xmlRelaxParserSetFlag	(xmlRelaxNGParserCtxt *ctxt,
						 int flag);

XMLPUBFUN void
		    xmlRelaxNGFreeParserCtxt	(xmlRelaxNGParserCtxt *ctxt);
XMLPUBFUN void
		    xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxt *ctxt,
					 xmlRelaxNGValidityErrorFunc err,
					 xmlRelaxNGValidityWarningFunc warn,
					 void *ctx);
XMLPUBFUN int
		    xmlRelaxNGGetParserErrors(xmlRelaxNGParserCtxt *ctxt,
					 xmlRelaxNGValidityErrorFunc *err,
					 xmlRelaxNGValidityWarningFunc *warn,
					 void **ctx);
```

[Assistant]
diff --git a/include/libxml/relaxng.h b/include/libxml/relaxng.h
index eafc660..099dacd 100644
--- a/include/libxml/relaxng.h
+++ b/include/libxml/relaxng.h
@@ -136,6 +136,10 @@ XMLPUBFUN int
 		    xmlRelaxParserSetFlag	(xmlRelaxNGParserCtxt *ctxt,
 						 int flag);

+XMLPUBFUN int
+		    xmlRelaxParserSetIncLImit	(xmlRelaxNGParserCtxt *ctxt,
+						 int limit);
+
 XMLPUBFUN void
 		    xmlRelaxNGFreeParserCtxt	(xmlRelaxNGParserCtxt *ctxt);
 XMLPUBFUN void
diff --git a/relaxng.c b/relaxng.c
index 1d74ba9..c0e94a3 100644
--- a/relaxng.c
+++ b/relaxng.c
@@ -18,6 +18,8 @@

 #ifdef LIBXML_RELAXNG_ENABLED

+#include <errno.h>
+#include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include <stddef.h>
@@ -44,6 +46,12 @@
 static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
     "http://relaxng.org/ns/structure/1.0";

+/*
+ * Default include limit, this can be override with RNG_INCLUDE_LIMIT
+ * env variable
+ */
+static const int _xmlRelaxNGIncludeLimit = 1000;
+
 #define IS_RELAXNG(node, typ)						\
    ((node != NULL) && (node->ns != NULL) &&				\
     (node->type == XML_ELEMENT_NODE) &&					\
@@ -218,6 +226,7 @@ struct _xmlRelaxNGParserCtxt {
     int incNr;                  /* Depth of the include parsing stack */
     int incMax;                 /* Max depth of the parsing stack */
     xmlRelaxNGIncludePtr *incTab;       /* array of incs */
+    int incLimit;               /* Include limit, to avoid stack-overflow on parse */

     int idref;                  /* requires idref checking */

@@ -1342,6 +1351,23 @@ xmlRelaxParserSetFlag(xmlRelaxNGParserCtxt *ctxt, int flags)
     return(0);
 }

+/**
+ * Semi private function used to set the include recursion limit to a
+ * parser context. Set to 0 to use the default value.
+ *
+ * @param ctxt  a RelaxNG parser context
+ * @param limit the new include depth limit
+ * @returns 0 if success and -1 in case of error
+ */
+int
+xmlRelaxParserSetIncLImit(xmlRelaxNGParserCtxt *ctxt, int limit)
+{
+    if (ctxt == NULL) return(-1);
+    if (limit < 0) return(-1);
+    ctxt->incLimit = limit;
+    return(0);
+}
+
 /************************************************************************
  *									*
  *			Document functions				*
@@ -1397,7 +1423,7 @@ xmlRelaxReadMemory(xmlRelaxNGParserCtxtPtr ctxt, const char *buf, int size) {
  *
  * @param ctxt  the parser context
  * @param value  the element doc
- * @returns 0 in case of error, the index in the stack otherwise
+ * @returns -1 in case of error, the index in the stack otherwise
  */
 static int
 xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
@@ -1411,9 +1437,15 @@ xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
                                                sizeof(ctxt->incTab[0]));
         if (ctxt->incTab == NULL) {
             xmlRngPErrMemory(ctxt);
-            return (0);
+            return (-1);
         }
     }
+    if (ctxt->incNr >= ctxt->incLimit) {
+        xmlRngPErr(ctxt, (xmlNodePtr)value->doc, XML_RNGP_PARSE_ERROR,
+                   "xmlRelaxNG: inclusion recursion limit reached\n", NULL, NULL);
+        return(-1);
+    }
+
     if (ctxt->incNr >= ctxt->incMax) {
         ctxt->incMax *= 2;
         ctxt->incTab =
@@ -1422,7 +1454,7 @@ xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
                                                 sizeof(ctxt->incTab[0]));
         if (ctxt->incTab == NULL) {
             xmlRngPErrMemory(ctxt);
-            return (0);
+            return (-1);
         }
     }
     ctxt->incTab[ctxt->incNr] = value;
@@ -1586,7 +1618,9 @@ xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar * URL,
     /*
      * push it on the stack
      */
-    xmlRelaxNGIncludePush(ctxt, ret);
+    if (xmlRelaxNGIncludePush(ctxt, ret) < 0) {
+        return (NULL);
+    }

     /*
      * Some preprocessing of the document content, this include recursing
@@ -7261,11 +7295,32 @@ xmlRelaxNGParse(xmlRelaxNGParserCtxt *ctxt)
     xmlDocPtr doc;
     xmlNodePtr root;

+    const char *include_limit_env = getenv("RNG_INCLUDE_LIMIT");
+
     xmlRelaxNGInitTypes();

     if (ctxt == NULL)
         return (NULL);

+    if (ctxt->incLimit == 0) {
+        ctxt->incLimit = _xmlRelaxNGIncludeLimit;
+        if (include_limit_env != NULL) {
+            char *strEnd;
+            unsigned long val = 0;
+            errno = 0;
+            val = strtoul(include_limit_env, &strEnd, 10);
+            if (errno != 0 || *strEnd != 0 || val > INT_MAX) {
+                xmlRngPErr(ctxt, NULL, XML_RNGP_PARSE_ERROR,
+                           "xmlRelaxNGParse: invalid RNG_INCLUDE_LIMIT %s\n",
+                           (const xmlChar*)include_limit_env,
+                           NULL);
+                return(NULL);
+            }
+            if (val)
+                ctxt->incLimit = val;
+        }
+    }
+
     /*
      * First step is to parse the input document into an DOM/Infoset
      */


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-0992: Denial-of-service vulnerability (CWE-400) in libxml2 2.13.9 XML catalog processing. This causes O(n^2) growth in catalog lookup cost when the resulting list is traversed repeatedly, enabling an attacker who controls catalog file content to cause unbounded CPU or memory consumption.

# Vulnerable source (catalog.c)
```c
		BAD_CAST "rewritePrefix", prefer, cgroup);
    } else if (xmlStrEqual(cur->name, BAD_CAST "delegatePublic")) {
	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_PUBLIC,
		BAD_CAST "delegatePublic", BAD_CAST "publicIdStartString",
		BAD_CAST "catalog", prefer, cgroup);
    } else if (xmlStrEqual(cur->name, BAD_CAST "delegateSystem")) {
	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_SYSTEM,
		BAD_CAST "delegateSystem", BAD_CAST "systemIdStartString",
		BAD_CAST "catalog", prefer, cgroup);
    } else if (xmlStrEqual(cur->name, BAD_CAST "uri")) {
	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_URI,
		BAD_CAST "uri", BAD_CAST "name",
		BAD_CAST "uri", prefer, cgroup);
    } else if (xmlStrEqual(cur->name, BAD_CAST "rewriteURI")) {
	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_REWRITE_URI,
		BAD_CAST "rewriteURI", BAD_CAST "uriStartString",
		BAD_CAST "rewritePrefix", prefer, cgroup);
    } else if (xmlStrEqual(cur->name, BAD_CAST "delegateURI")) {
	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_DELEGATE_URI,
		BAD_CAST "delegateURI", BAD_CAST "uriStartString",
		BAD_CAST "catalog", prefer, cgroup);
    } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {
	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,
		BAD_CAST "nextCatalog", NULL,
		BAD_CAST "catalog", prefer, cgroup);
    }
    if (entry != NULL) {
        if (parent != NULL) {
	    entry->parent = parent;
	    if (parent->children == NULL)
		parent->children = entry;
	    else {
		xmlCatalogEntryPtr prev;

		prev = parent->children;
		while (prev->next != NULL)
		    prev = prev->next;
		prev->next = entry;
	    }
	}
```

[Assistant]
diff --git a/catalog.c b/catalog.c
index 46b877e..fa6d77c 100644
--- a/catalog.c
+++ b/catalog.c
@@ -1223,9 +1223,27 @@ xmlParseXMLCatalogNode(xmlNodePtr cur, xmlCatalogPrefer prefer,
 		BAD_CAST "delegateURI", BAD_CAST "uriStartString",
 		BAD_CAST "catalog", prefer, cgroup);
     } else if (xmlStrEqual(cur->name, BAD_CAST "nextCatalog")) {
+	xmlCatalogEntryPtr prev = parent->children;
+
 	entry = xmlParseXMLCatalogOneNode(cur, XML_CATA_NEXT_CATALOG,
 		BAD_CAST "nextCatalog", NULL,
 		BAD_CAST "catalog", prefer, cgroup);
+	/* Avoid duplication of nextCatalog */
+	while (prev != NULL) {
+	    if ((prev->type == XML_CATA_NEXT_CATALOG) &&
+		(xmlStrEqual (prev->URL, entry->URL)) &&
+		(xmlStrEqual (prev->value, entry->value)) &&
+		(prev->prefer == entry->prefer) &&
+		(prev->group == entry->group)) {
+		    if (xmlDebugCatalogs)
+			xmlCatalogPrintDebug(
+			    "Ignoring repeated nextCatalog %s\n", entry->URL);
+		    xmlFreeCatalogEntry(entry, NULL);
+		    entry = NULL;
+		    break;
+	    }
+	    prev = prev->next;
+	}
     }
     if (entry != NULL) {
         if (parent != NULL) {


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-10275: Buffer overflow (CWE-122) in OpenSC 0.27.1 pkcs11-tool.

# Vulnerable source (src/tools/pkcs11-tool.c)
```c
			if (strcmp(opt_uri->type, "cert") == 0) {
				opt_object_class = CKO_CERTIFICATE;
				opt_object_class_str = "cert";
			} else if (strcmp(opt_uri->type, "private") == 0) {
				opt_object_class = CKO_PRIVATE_KEY;
				opt_object_class_str = "privkey";
			} else if (strcmp(opt_uri->type, "secret-key") == 0) {
				opt_object_class = CKO_SECRET_KEY;
				opt_object_class_str = "secrkey";
			} else if (strcmp(opt_uri->type, "public") == 0) {
				opt_object_class = CKO_PUBLIC_KEY;
				opt_object_class_str = "pubkey,";
			} else if (strcmp(opt_uri->type, "data") == 0) {
				opt_object_class = CKO_DATA;
				opt_object_class_str = "data";
			} else {
				fprintf(stderr, "Unsupported object type \"%s\" specified in PKCS#11 URI\n", opt_uri->type);
				util_print_usage_and_die(app_name, options, option_help, NULL);
			}
		}
		if (opt_uri->id) {
			opt_object_id_len = opt_uri->id_len;
			memcpy(opt_object_id, opt_uri->id, opt_object_id_len);
		}
	}

	if (!opt_module) {
		opt_module = DEFAULT_PKCS11_PROVIDER;
	}
#ifdef _WIN32
	expanded_len = PATH_MAX;
	expanded_len = ExpandEnvironmentStringsA(opt_module, expanded_val, expanded_len);
	if (0 < expanded_len && expanded_len < sizeof expanded_val)
		opt_module = expanded_val;
#endif

#ifndef ENABLE_SHARED
	if (strcmp(opt_module, DEFAULT_PKCS11_PROVIDER) == 0)
		p11 = &pkcs11_function_list_3_0;
	else
        /* ... */
	f = fopen(opt_file_to_write, "rb");
	if (f == NULL)
		util_fatal("Couldn't open file \"%s\"", opt_file_to_write);
	fclose(f);

	/* Get for a not-yet-existing ID */
	while(find_object(session, CKO_PRIVATE_KEY, &priv_key, id, id_len, NULL, 0))
		id[0]++;

	printf("\n*** Generating a %s key pair ***\n", opt_key_type);

	if (!gen_keypair(slot, session, &pub_key, &priv_key, opt_key_type)) {
		printf("ERR: cannot generate new key pair\n");
		return session;
	}

	tmp = getID(session, priv_key, &i);
	if (i == 0) {
		fprintf(stderr, "ERR: newly generated private key has no (or an empty) CKA_ID\n");
		return session;
	}
	opt_object_id_len = (size_t) i;
	memcpy(opt_object_id, tmp, opt_object_id_len);

	/* This is done in NSS */
	getMODULUS(session, priv_key, &mod_len);
	if (mod_len < 5 || mod_len > 10000) { /* should be reasonable limits */
		fprintf(stderr, "ERR: GetAttribute(privkey, CKA_MODULUS) doesn't seem to work\n");
		return session;
	}

	printf("\n*** Changing the CKA_ID of private and public key into one of 20 bytes ***\n");

	rv = p11->C_SetAttributeValue(session, priv_key, attribs, 1);
	if (rv != CKR_OK)
		p11_fatal("C_SetAttributeValue(priv_key)", rv);

	rv = p11->C_SetAttributeValue(session, pub_key, attribs, 1);
	if (rv != CKR_OK)
		p11_fatal("C_SetAttributeValue(pub_key)", rv);
```

[Assistant]
diff --git a/src/tools/pkcs11-tool.c b/src/tools/pkcs11-tool.c
--- a/src/tools/pkcs11-tool.c
+++ b/src/tools/pkcs11-tool.c
@@ -1342,6 +1342,8 @@ int main(int argc, char * argv[])
 		}
 		if (opt_uri->id) {
 			opt_object_id_len = opt_uri->id_len;
+			if (opt_object_id_len > sizeof(opt_object_id))
+				util_fatal("URI's object ID too long");
 			memcpy(opt_object_id, opt_uri->id, opt_object_id_len);
 		}
 	}
@@ -9608,6 +9610,10 @@ static CK_SESSION_HANDLE test_kpgen_certwrite(CK_SLOT_ID slot, CK_SESSION_HANDLE
 		return session;
 	}
 	opt_object_id_len = (size_t) i;
+	if (opt_object_id_len > sizeof(opt_object_id)) {
+		fprintf(stderr, "ERR: object ID too long\n");
+		return session;
+	}
 	memcpy(opt_object_id, tmp, opt_object_id_len);

 	/* This is done in NSS */


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-1144: quickjs-ng contained an out-of-bounds memory access in the atomic operations handler. The function that resolved an index into a typed array buffer for atomic load, store, and compare-exchange operations computed the byte pointer to the buffer before checking whether a user-supplied .valueOf() callback had resized or invalidated the underlying allocation. An attacker who controlled the JavaScript runtime could trigger heap corruption by crafting an array whose backing buffer was invalidated by a side-effecting callback, potentially leading to arbitrary code execution.

# Vulnerable source (quickjs.c)
```c
        a = (int8_t)a;
        goto done;
    case JS_CLASS_UINT8_ARRAY:
        a = (uint8_t)a;
        goto done;
    case JS_CLASS_INT16_ARRAY:
        a = (int16_t)a;
        goto done;
    case JS_CLASS_UINT16_ARRAY:
        a = (uint16_t)a;
        goto done;
    case JS_CLASS_INT32_ARRAY:
    done:
        ret = js_int32(a);
        break;
    case JS_CLASS_UINT32_ARRAY:
        ret = js_uint32(a);
        break;
    case JS_CLASS_BIG_INT64_ARRAY:
        ret = JS_NewBigInt64(ctx, a);
        break;
    case JS_CLASS_BIG_UINT64_ARRAY:
        ret = JS_NewBigUint64(ctx, a);
        break;
    default:
        abort();
    }
    return ret;
}

static JSValue js_atomics_store(JSContext *ctx,
                                JSValueConst this_obj,
                                int argc, JSValueConst *argv)
{
    int size_log2;
    void *ptr;
    JSValue ret;
    JSArrayBuffer *abuf;

    ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL,
                             argv[0], argv[1], 0);
    if (!ptr)
        return JS_EXCEPTION;
    if (size_log2 == 3) {
        int64_t v64;
        ret = JS_ToBigIntFree(ctx, js_dup(argv[2]));
        if (JS_IsException(ret))
            return ret;
        if (JS_ToBigInt64(ctx, &v64, ret)) {
            JS_FreeValue(ctx, ret);
            return JS_EXCEPTION;
        }
        if (abuf->detached)
            return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
        atomic_store((_Atomic uint64_t *)ptr, v64);
    } else {
        uint32_t v;
        /* XXX: spec, would be simpler to return the written value */
        ret = JS_ToIntegerFree(ctx, js_dup(argv[2]));
        if (JS_IsException(ret))
            return ret;
        if (JS_ToUint32(ctx, &v, ret)) {
            JS_FreeValue(ctx, ret);
            return JS_EXCEPTION;
        }
        if (abuf->detached)
            return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
        switch(size_log2) {
        case 0:
            atomic_store((_Atomic uint8_t *)ptr, v);
            break;
        case 1:
            atomic_store((_Atomic uint16_t *)ptr, v);
            break;
        case 2:
            atomic_store((_Atomic uint32_t *)ptr, v);
            break;
        default:
            abort();
        }
    }
    return ret;
}

static JSValue js_atomics_isLockFree(JSContext *ctx,
                                     JSValueConst this_obj,
                                     int argc, JSValueConst *argv)
{
    int v, ret;
    if (JS_ToInt32Sat(ctx, &v, argv[0]))
        return JS_EXCEPTION;
    ret = (v == 1 || v == 2 || v == 4 || v == 8);
    return js_bool(ret);
}

typedef struct JSAtomicsWaiter {
    struct list_head link;
    bool linked;
    js_cond_t cond;
    int32_t *ptr;
} JSAtomicsWaiter;

static js_once_t js_atomics_once = JS_ONCE_INIT;
static js_mutex_t js_atomics_mutex;
static struct list_head js_atomics_waiter_list =
    LIST_HEAD_INIT(js_atomics_waiter_list);

// no-op: Atomics.pause() is not allowed to block or yield to another
// thread, only to hint the CPU that it should back off for a bit;
// the amount of work we do here is a good enough substitute
static JSValue js_atomics_pause(JSContext *ctx, JSValueConst this_obj,
                                int argc, JSValueConst *argv)
{
    double d;

    if (argc > 0) {
        switch (JS_VALUE_GET_TAG(argv[0])) {
        case JS_TAG_FLOAT64: // accepted if and only if fraction == 0.0
            d = JS_VALUE_GET_FLOAT64(argv[0]);
            if (isfinite(d))
                if (0 == modf(d, &d))
                    break;
            // fallthru
        default:
            return JS_ThrowTypeError(ctx, "not an integral number");
        case JS_TAG_UNDEFINED:
        case JS_TAG_INT:
            break;
        }
    }
    return JS_UNDEFINED;
}

static JSValue js_atomics_wait(JSContext *ctx,
                               JSValueConst this_obj,
                               int argc, JSValueConst *argv)
{
    int64_t v;
    int32_t v32;
    void *ptr;
    int64_t timeout;
    JSAtomicsWaiter waiter_s, *waiter;
    int ret, size_log2, res;
    double d;

    ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL,
                             argv[0], argv[1], 2);
    if (!ptr)
        return JS_EXCEPTION;
    if (size_log2 == 3) {
        if (JS_ToBigInt64(ctx, &v, argv[2]))
            return JS_EXCEPTION;
    } else {
        if (JS_ToInt32(ctx, &v32, argv[2]))
            return JS_EXCEPTION;
        v = v32;
    }
    if (JS_ToFloat64(ctx, &d, argv[3]))
        return JS_EXCEPTION;
    if (isnan(d) || d >= 0x1p63)
        timeout = INT64_MAX;
    else if (d < 0)
        timeout = 0;
    else
        timeout = (int64_t)d;
    if (!ctx->rt->can_block)
        return JS_ThrowTypeError(ctx, "cannot block in this thread");

    /* XXX: inefficient if large number of waiters, should hash on
       'ptr' value */
    js_mutex_lock(&js_atomics_mutex);
    if (size_log2 == 3) {
        res = *(int64_t *)ptr != v;
    } else {
        res = *(int32_t *)ptr != v;
    }
    if (res) {
        js_mutex_unlock(&js_atomics_mutex);
        return JS_AtomToString(ctx, JS_ATOM_not_equal);
    }

    waiter = &waiter_s;
    waiter->ptr = ptr;
    js_cond_init(&waiter->cond);
    waiter->linked = true;
    list_add_tail(&waiter->link, &js_atomics_waiter_list);

    if (timeout == INT64_MAX) {
        js_cond_wait(&waiter->cond, &js_atomics_mutex);
        ret = 0;
    } else {
        ret = js_cond_timedwait(&waiter->cond, &js_atomics_mutex, timeout * 1e6 /* to ns */);
    }
    if (waiter->linked)
        list_del(&waiter->link);
    js_mutex_unlock(&js_atomics_mutex);
    js_cond_destroy(&waiter->cond);
    if (ret == -1) {
        return JS_AtomToString(ctx, JS_ATOM_timed_out);
    } else {
        return JS_AtomToString(ctx, JS_ATOM_ok);
    }
}

static JSValue js_atomics_notify(JSContext *ctx,
                                 JSValueConst this_obj,
                                 int argc, JSValueConst *argv)
{
    struct list_head *el, *el1, waiter_list;
    int32_t count, n;
    void *ptr;
    JSAtomicsWaiter *waiter;
    JSArrayBuffer *abuf;

    ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1);
    if (!ptr)
        return JS_EXCEPTION;

    if (JS_IsUndefined(argv[2])) {
        count = INT32_MAX;
    } else {
        if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0))
            return JS_EXCEPTION;
    }
    if (abuf->detached)
        return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);

    n = 0;
    if (abuf->shared && count > 0) {
        js_mutex_lock(&js_atomics_mutex);
        init_list_head(&waiter_list);
        list_for_each_safe(el, el1, &js_atomics_waiter_list) {
            waiter = list_entry(el, JSAtomicsWaiter, link);
            if (waiter->ptr == ptr) {
                list_del(&waiter->link);
                waiter->linked = false;
                list_add_tail(&waiter->link, &waiter_list);
                n++;
                if (n >= count)
                    break;
            }
        }
        list_for_each(el, &waiter_list) {
            waiter = list_entry(el, JSAtomicsWaiter, link);
            js_cond_signal(&waiter->cond);
        }
        js_mutex_unlock(&js_atomics_mutex);
    }
    return js_int32(n);
}

static const JSCFunctionListEntry js_atomics_funcs[] = {
    JS_CFUNC_MAGIC_DEF("add", 3, js_atomics_op, ATOMICS_OP_ADD ),
    JS_CFUNC_MAGIC_DEF("and", 3, js_atomics_op, ATOMICS_OP_AND ),
    JS_CFUNC_MAGIC_DEF("or", 3, js_atomics_op, ATOMICS_OP_OR ),
    JS_CFUNC_MAGIC_DEF("sub", 3, js_atomics_op, ATOMICS_OP_SUB ),
    JS_CFUNC_MAGIC_DEF("xor", 3, js_atomics_op, ATOMICS_OP_XOR ),
    JS_CFUNC_MAGIC_DEF("exchange", 3, js_atomics_op, ATOMICS_OP_EXCHANGE ),
    JS_CFUNC_MAGIC_DEF("compareExchange", 4, js_atomics_op, ATOMICS_OP_COMPARE_EXCHANGE ),
    JS_CFUNC_MAGIC_DEF("load", 2, js_atomics_op, ATOMICS_OP_LOAD ),
    JS_CFUNC_DEF("store", 3, js_atomics_store ),
    JS_CFUNC_DEF("isLockFree", 1, js_atomics_isLockFree ),
    JS_CFUNC_DEF("pause", 0, js_atomics_pause ),
    JS_CFUNC_DEF("wait", 4, js_atomics_wait ),
    JS_CFUNC_DEF("notify", 3, js_atomics_notify ),
    JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Atomics", JS_PROP_CONFIGURABLE ),
};

static const JSCFunctionListEntry js_atomics_obj[] = {
    JS_OBJECT_DEF("Atomics", js_atomics_funcs, countof(js_atomics_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
};

static void js__atomics_init(void) {
    js_mutex_init(&js_atomics_mutex);
}

/* TODO(saghul) make this public and not dependent on typed arrays? */
void JS_AddIntrinsicAtomics(JSContext *ctx)
{
    js_once(&js_atomics_once, js__atomics_init);

    /* add Atomics as autoinit object */
    JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_atomics_obj, countof(js_atomics_obj));
}

#endif /* CONFIG_ATOMICS */

void JS_AddIntrinsicTypedArrays(JSContext *ctx)
{
    JSValue typed_array_base_proto, typed_array_base_func;
    JSValue array_buffer_func, shared_array_buffer_func;
    int i;

    ctx->class_proto[JS_CLASS_ARRAY_BUFFER] = JS_NewObject(ctx);
    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_BUFFER],
                               js_array_buffer_proto_funcs,
                               countof(js_array_buffer_proto_funcs));

    array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "ArrayBuffer",
                                                 js_array_buffer_constructor, 1,
                                                 ctx->class_proto[JS_CLASS_ARRAY_BUFFER]);
    JS_SetPropertyFunctionList(ctx, array_buffer_func,
                               js_array_buffer_funcs,
                               countof(js_array_buffer_funcs));

    ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER] = JS_NewObject(ctx);
    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER],
                               js_shared_array_buffer_proto_funcs,
                               countof(js_shared_array_buffer_proto_funcs));

    shared_array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "SharedArrayBuffer",
                                                 js_shared_array_buffer_constructor, 1,
                                                 ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER]);
    JS_SetPropertyFunctionList(ctx, shared_array_buffer_func,
                               js_shared_array_buffer_funcs,
                               countof(js_shared_array_buffer_funcs));

    typed_array_base_proto = JS_NewObject(ctx);
    JS_SetPropertyFunctionList(ctx, typed_array_base_proto,
                               js_typed_array_base_proto_funcs,
                               countof(js_typed_array_base_proto_funcs));

    /* TypedArray.prototype.toString must be the same object as Array.prototype.toString */
    JSValue obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_toString);
    /* XXX: should use alias method in JSCFunctionListEntry */ //@@@
    JS_DefinePropertyValue(ctx, typed_array_base_proto, JS_ATOM_toString, obj,
                           JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);

    typed_array_base_func = JS_NewCFunction(ctx, js_typed_array_base_constructor,
                                            "TypedArray", 0);
    JS_SetPropertyFunctionList(ctx, typed_array_base_func,
                               js_typed_array_base_funcs,
                               countof(js_typed_array_base_funcs));
    JS_SetConstructor(ctx, typed_array_base_func, typed_array_base_proto);
    JS_SetConstructorBit(ctx, typed_array_base_func, true);

    /* Used to squelch a -Wcast-function-type warning. */
    JSCFunctionType ft = { .generic_magic = js_typed_array_constructor };
    for(i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) {
        JSValue func_obj;
        char buf[ATOM_GET_STR_BUF_SIZE];
        const char *name;

        ctx->class_proto[i] = JS_NewObjectProto(ctx, typed_array_base_proto);
        JS_DefinePropertyValueStr(ctx, ctx->class_proto[i],
                                  "BYTES_PER_ELEMENT",
                                  js_int32(1 << typed_array_size_log2(i)),
                                  0);
        name = JS_AtomGetStr(ctx, buf, sizeof(buf),
                             JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY);
        func_obj = JS_NewCFunction3(ctx, ft.generic,
                                    name, 3, JS_CFUNC_constructor_magic, i,
                                    typed_array_base_func);
        JS_NewGlobalCConstructor2(ctx, func_obj, name, ctx->class_proto[i]);
        JS_DefinePropertyValueStr(ctx, func_obj,
                                  "BYTES_PER_ELEMENT",
                                  js_int32(1 << typed_array_size_log2(i)),
                                  0);
    }
    JS_FreeValue(ctx, typed_array_base_proto);
    JS_FreeValue(ctx, typed_array_base_func);

    /* DataView */
    ctx->class_proto[JS_CLASS_DATAVIEW] = JS_NewObject(ctx);
    JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATAVIEW],
                               js_dataview_proto_funcs,
                               countof(js_dataview_proto_funcs));
    JS_NewGlobalCConstructorOnly(ctx, "DataView",
                                 js_dataview_constructor, 1,
                                 ctx->class_proto[JS_CLASS_DATAVIEW]);
    /* Atomics */
#ifdef CONFIG_ATOMICS
    JS_AddIntrinsicAtomics(ctx);
#endif
}

/* Performance */

static double js__now_ms(void)
{
    return js__hrtime_ns() / 1e6;
}

static JSValue js_perf_now(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
    return js_float64(js__now_ms() - ctx->time_origin);
}

static const JSCFunctionListEntry js_perf_proto_funcs[] = {
    JS_CFUNC_DEF2("now", 0, js_perf_now, JS_PROP_ENUMERABLE),
};

void JS_AddPerformance(JSContext *ctx)
{
    ctx->time_origin = js__now_ms();

    JSValue performance = JS_NewObject(ctx);
    JS_SetPropertyFunctionList(ctx, performance, js_perf_proto_funcs, countof(js_perf_proto_funcs));
    JS_DefinePropertyValueStr(ctx, performance, "timeOrigin",
                           js_float64(ctx->time_origin),
                           JS_PROP_ENUMERABLE);
    JS_DefinePropertyValueStr(ctx, ctx->global_obj, "performance",
                           js_dup(performance),
                           JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | JS_PROP_CONFIGURABLE);
    JS_FreeValue(ctx, performance);
}

/* Equality comparisons and sameness */
int JS_IsEqual(JSContext *ctx, JSValueConst op1, JSValueConst op2)
{
    JSValue sp[2] = { js_dup(op1), js_dup(op2) };
    if (js_eq_slow(ctx, endof(sp), 0))
        return -1;
    return JS_VALUE_GET_BOOL(sp[0]);
}

bool JS_IsStrictEqual(JSContext *ctx, JSValueConst op1, JSValueConst op2)
{
    return js_strict_eq2(ctx, js_dup(op1), js_dup(op2), JS_EQ_STRICT);
}

bool JS_IsSameValue(JSContext *ctx, JSValueConst op1, JSValueConst op2)
{
    return js_same_value(ctx, op1, op2);
}

bool JS_IsSameValueZero(JSContext *ctx, JSValueConst op1, JSValueConst op2)
{
    return js_same_value_zero(ctx, op1, op2);
}

/* WeakRef */

typedef struct JSWeakRefData {
    JSValueConst target;
    JSValue obj;
} JSWeakRefData;

static JSWeakRefData js_weakref_sentinel;

static void js_weakref_finalizer(JSRuntime *rt, JSValueConst val)
{
    JSWeakRefData *wrd = JS_GetOpaque(val, JS_CLASS_WEAK_REF);
    if (!wrd || wrd == &js_weakref_sentinel)
        return;

    /* Delete weak ref */
    JSWeakRefRecord **pwr, *wr;

    pwr = get_first_weak_ref(wrd->target);
    for(;;) {
        wr = *pwr;
        assert(wr != NULL);
        if (wr->kind == JS_WEAK_REF_KIND_WEAK_REF && wr->u.weak_ref_data == wrd)
            break;
        pwr = &wr->next_weak_ref;
    }
    *pwr = wr->next_weak_ref;
    js_free_rt(rt, wrd);
    js_free_rt(rt, wr);
}

static JSValue js_weakref_constructor(JSContext *ctx, JSValueConst new_target,
                                      int argc, JSValueConst *argv)
{
    if (JS_IsUndefined(new_target))
        return JS_ThrowTypeError(ctx, "constructor requires 'new'");
    JSValueConst arg = argv[0];
    if (!is_valid_weakref_target(arg))
        return JS_ThrowTypeError(ctx, "invalid target");
    // TODO(saghul): short-circuit if the refcount is 1?
    JSValue obj = js_create_from_ctor(ctx, new_target, JS_CLASS_WEAK_REF);
    if (JS_IsException(obj))
        return JS_EXCEPTION;
    JSWeakRefData *wrd = js_malloc(ctx, sizeof(*wrd));
    if (!wrd) {
        JS_FreeValue(ctx, obj);
        return JS_EXCEPTION;
    }
    JSWeakRefRecord *wr = js_malloc(ctx, sizeof(*wr));
    if (!wr) {
        JS_FreeValue(ctx, obj);
        js_free(ctx, wrd);
        return JS_EXCEPTION;
    }
    wrd->target = arg;
    wrd->obj = obj;
```

[Assistant]
diff --git a/quickjs.c b/quickjs.c
index 27b31c15f..04388e45f 100644
--- a/quickjs.c
+++ b/quickjs.c
@@ -57026,19 +57026,16 @@ typedef enum AtomicsOpEnum {
     ATOMICS_OP_LOAD,
 } AtomicsOpEnum;

-static void *js_atomics_get_ptr(JSContext *ctx,
-                                JSArrayBuffer **pabuf,
-                                int *psize_log2, JSClassID *pclass_id,
-                                JSValueConst obj, JSValueConst idx_val,
-                                int is_waitable)
+static JSObject *js_atomics_get_buf(JSContext *ctx,
+                                    JSValueConst obj, JSValueConst idx_val,
+                                    JSArrayBuffer **pabuf,
+                                    uint64_t *pindex, int is_waitable)
 {
     JSObject *p;
     JSTypedArray *ta;
     JSArrayBuffer *abuf;
-    void *ptr;
     uint64_t idx;
     bool err;
-    int size_log2;

     if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
         goto fail;
@@ -57074,15 +57071,10 @@ static void *js_atomics_get_ptr(JSContext *ctx,
         JS_ThrowRangeError(ctx, "out-of-bound access");
         return NULL;
     }
-    size_log2 = typed_array_size_log2(p->class_id);
-    ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
     if (pabuf)
         *pabuf = abuf;
-    if (psize_log2)
-        *psize_log2 = size_log2;
-    if (pclass_id)
-        *pclass_id = p->class_id;
-    return ptr;
+    *pindex = idx;
+    return p;
 }

 static JSValue js_atomics_op(JSContext *ctx,
@@ -57090,16 +57082,15 @@ static JSValue js_atomics_op(JSContext *ctx,
                              int argc, JSValueConst *argv, int op)
 {
     int size_log2;
-    uint64_t v, a, rep_val;
+    uint64_t v, a, rep_val, idx;
     void *ptr;
+    JSObject *p;
     JSValue ret;
-    JSClassID class_id;
-    JSArrayBuffer *abuf;

-    ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, &class_id,
-                             argv[0], argv[1], 0);
-    if (!ptr)
+    p = js_atomics_get_buf(ctx, argv[0], argv[1], NULL, &idx, 0);
+    if (!p)
         return JS_EXCEPTION;
+    size_log2 = typed_array_size_log2(p->class_id);
     rep_val = 0;
     if (op == ATOMICS_OP_LOAD) {
         v = 0;
@@ -57125,10 +57116,14 @@ static JSValue js_atomics_op(JSContext *ctx,
 				rep_val = v32;
 			}
         }
-        if (abuf->detached)
-            return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
    }

+    /* check if an evil .valueOf has resized or detached the array */
+    if (idx >= p->u.array.count)
+        return JS_ThrowRangeError(ctx, "out-of-bound access");
+
+    ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
+
    switch(op | (size_log2 << 3)) {

 #define OP(op_name, func_name)                          \
@@ -57196,7 +57191,7 @@ static JSValue js_atomics_op(JSContext *ctx,
         abort();
     }

-    switch(class_id) {
+    switch(p->class_id) {
     case JS_CLASS_INT8_ARRAY:
         a = (int8_t)a;
         goto done;
@@ -57233,14 +57228,15 @@ static JSValue js_atomics_store(JSContext *ctx,
                                 int argc, JSValueConst *argv)
 {
     int size_log2;
+    uint64_t idx;
     void *ptr;
+    JSObject *p;
     JSValue ret;
-    JSArrayBuffer *abuf;

-    ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL,
-                             argv[0], argv[1], 0);
-    if (!ptr)
+    p = js_atomics_get_buf(ctx, argv[0], argv[1], NULL, &idx, 0);
+    if (!p)
         return JS_EXCEPTION;
+    size_log2 = typed_array_size_log2(p->class_id);
     if (size_log2 == 3) {
         int64_t v64;
         ret = JS_ToBigIntFree(ctx, js_dup(argv[2]));
@@ -57250,8 +57246,11 @@ static JSValue js_atomics_store(JSContext *ctx,
             JS_FreeValue(ctx, ret);
             return JS_EXCEPTION;
         }
-        if (abuf->detached)
-            return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+        /* check if an evil .valueOf has resized or detached the array */
+        if (idx >= p->u.array.count) {
+            return JS_ThrowRangeError(ctx, "out-of-bound access");
+        }
+        ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
         atomic_store((_Atomic uint64_t *)ptr, v64);
     } else {
         uint32_t v;
@@ -57263,8 +57262,11 @@ static JSValue js_atomics_store(JSContext *ctx,
             JS_FreeValue(ctx, ret);
             return JS_EXCEPTION;
         }
-        if (abuf->detached)
-            return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+        /* check if an evil .valueOf has resized or detached the array */
+        if (idx >= p->u.array.count) {
+            return JS_ThrowRangeError(ctx, "out-of-bound access");
+        }
+        ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
         switch(size_log2) {
         case 0:
             atomic_store((_Atomic uint8_t *)ptr, v);
@@ -57339,14 +57341,16 @@ static JSValue js_atomics_wait(JSContext *ctx,
     int32_t v32;
     void *ptr;
     int64_t timeout;
+    uint64_t idx;
+    JSObject *p;
     JSAtomicsWaiter waiter_s, *waiter;
     int ret, size_log2, res;
     double d;

-    ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL,
-                             argv[0], argv[1], 2);
-    if (!ptr)
+    p = js_atomics_get_buf(ctx, argv[0], argv[1], NULL, &idx, 2);
+    if (!p)
         return JS_EXCEPTION;
+    size_log2 = typed_array_size_log2(p->class_id);
     if (size_log2 == 3) {
         if (JS_ToBigInt64(ctx, &v, argv[2]))
             return JS_EXCEPTION;
@@ -57366,6 +57370,12 @@ static JSValue js_atomics_wait(JSContext *ctx,
     if (!ctx->rt->can_block)
         return JS_ThrowTypeError(ctx, "cannot block in this thread");

+    /* check if an evil .valueOf has resized or detached the array */
+    if (idx >= p->u.array.count)
+        return JS_ThrowRangeError(ctx, "out-of-bound access");
+
+    ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
+
     /* XXX: inefficient if large number of waiters, should hash on
        'ptr' value */
     js_mutex_lock(&js_atomics_mutex);
@@ -57407,14 +57417,18 @@ static JSValue js_atomics_notify(JSContext *ctx,
                                  int argc, JSValueConst *argv)
 {
     struct list_head *el, *el1, waiter_list;
+    int size_log2;
     int32_t count, n;
     void *ptr;
-    JSAtomicsWaiter *waiter;
+    uint64_t idx;
+    JSObject *p;
     JSArrayBuffer *abuf;
+    JSAtomicsWaiter *waiter;

-    ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1);
-    if (!ptr)
+    p = js_atomics_get_buf(ctx, argv[0], argv[1], &abuf, &idx, 1);
+    if (!p)
         return JS_EXCEPTION;
+    size_log2 = typed_array_size_log2(p->class_id);

     if (JS_IsUndefined(argv[2])) {
         count = INT32_MAX;
@@ -57422,11 +57436,15 @@ static JSValue js_atomics_notify(JSContext *ctx,
         if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0))
             return JS_EXCEPTION;
     }
-    if (abuf->detached)
-        return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+
+    /* check if an evil .valueOf has resized or detached the array */
+    if (idx >= p->u.array.count)
+        return JS_ThrowRangeError(ctx, "out-of-bound access");

     n = 0;
     if (abuf->shared && count > 0) {
+        ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
+
         js_mutex_lock(&js_atomics_mutex);
         init_list_head(&waiter_list);
         list_for_each_safe(el, el1, &js_atomics_waiter_list) {


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-11625: The Bytes::Random::Secure Perl module does not detect when a process has been forked. After a fork, the child process inherits the parent's pseudorandom number generator (PRNG) state and will produce the same sequence of bytes as the parent, breaking the cryptographic security guarantees the module is meant to provide. An attacker who can observe the output of both parent and child processes can recover the full PRNG state.

# Vulnerable source (lib/Bytes/Random/Secure.pm)
```perl
    }
  }
  return %args;
}

# Round bits parameter to nearest greater or equal 32-bit "long".
sub _round_bits_to_ge_32 {
  my( $self, $bits ) = @_;
  my $remainder = $bits % 32;
  return $bits if $remainder == 0;
  carp "Bits field must be a multiple of 32.  Rounding up.";
  return $bits + 32 - $remainder;
}

# Constrain bits argument to a reasonable range.
sub _constrain_bits {
  my( $self, $bits, $min, $max ) = @_;

  if( $bits < $min ) {
    carp "Bits field must be >= 64 (two longs). Rounding up.";
    $bits = $min;
  }
  elsif( $bits > $max ) {
    carp "Bits field must be <= 8192 (256 longs). Rounding down.";
    $bits = $max;
  }
  # No need for an 'else' here.

  return $bits;
}

# Build attributes set by new().  Any not explicitly set will use defaults
# as described in the constant OO_ATTRIBS.
sub _build_attributes {
    my ( $self, $args ) = @_;

    while ( my ( $arg, $default ) = each %{ OO_ATTRIBS() } ) {
      $self->{$arg} = exists $args->{$arg} ? $args->{$arg} : $default;
    }

    $self->{_RNG} = undef;    # Lazy initialization.
    return $self;
}

# Get a seed and use it to instantiate a RNG.
# Note: Currently we specify only Math::Random::ISAAC.  However, the PRNG
# object attribute may be used in the future to specify alternate RNG's.
sub _instantiate_rng {
    my $self = shift;

    my ( %seed_opts ) = $self->_build_seed_options;
    my @seeds = $self->_generate_seed( %seed_opts );
    $self->{_RNG} = Math::Random::ISAAC->new(@seeds);

    return $self->{_RNG};
}

# Set up seed options for Crypt::Random::Seed
sub _build_seed_options {
  my( $self ) = @_;

  my %crs_opts;

  # CRYPT_RANDOM_SEED_OPTS enumerates the options that Crypt::Random::Seed
  # supports.  We have already built object attributes for those options.
  foreach my $opt ( @{ CRYPT_RANDOM_SEED_OPTS() } ) {
      $crs_opts{$opt} = $self->{$opt} if defined $self->{$opt};
  }

  return %crs_opts;
}

# Use Crypt::Random::Seed to generate some high-quality long int
# seeds for Math::Random::ISAAC.
sub _generate_seed {
    my ( $self, %options_hash ) = @_;

    my $seed_size = $self->{Bits} / 32;
    my $source = Crypt::Random::Seed->new(%options_hash);

    croak 'Unable to obtain a strong seed source from Crypt::Random::Seed.'
      unless defined $source;

    return $source->random_values($seed_size); # List of unsigned longs.
}

# Validate that we are getting an integer >= 0.
# If not, throw an exception.
sub _validate_int {
  my( $self, $input ) = @_;
  croak "Byte count must be a positive integer."
    unless    looks_like_number( $input )
           && $input == int( $input )
           && $input >= 0;
  return 1;
}

# Random bytes string.
sub bytes {
  my( $self, $bytes ) = @_;
  $bytes = defined $bytes ? $bytes : 0; # Default to zero bytes.
  $self->_validate_int( $bytes ); # Throws on violation.

  $self->_instantiate_rng unless defined $self->{_RNG};

  my $str = '';

  while ( $bytes >= 4 ) {                  # Utilize irand()'s 32 bits.
    $str .= pack( "L", $self->{_RNG}->irand );
    $bytes -= 4;
  }

  if ( $bytes > 0 ) {
    my $rval = $self->{_RNG}->irand;

    $str .= pack( "S", ( $rval >> 8 ) & 0xFFFF )
      if $bytes >= 2;                    # 16 bits.
    $str .= pack( "C", $rval & 0xFF ) if $bytes % 2;    # 8 bits.

  }
  return $str;
}

# Base64 encoding of random byte string.
sub bytes_base64 {
  my ( $self, $bytes, $eol ) = @_;
  return encode_base64( $self->bytes($bytes), defined($eol) ? $eol : qq{\n} );
}

# Hex digits representing random byte string (No whitespace, no '0x').
sub bytes_hex {
  my ( $self, $bytes ) = @_;
  return unpack 'H*', $self->bytes($bytes);
}

# Quoted Printable representation of random byte string.
sub bytes_qp {
  my ( $self, $bytes, $eol ) = @_;
  return encode_qp $self->bytes($bytes), defined($eol) ? $eol : qq{\n}, 1;
}

sub string_from {
  my( $self, $bag, $bytes ) = @_;
  $bag   = defined $bag   ? $bag   : '';
  $bytes = defined $bytes ? $bytes : 0;
  my $range = length $bag;

  $self->_validate_int( $bytes );

  croak "Bag's size must be at least 1 character."
    if $range < 1;

  my $rand_bytes = q{};              # We need an empty (and defined) string.

  for my $random ( $self->_ranged_randoms( $range, $bytes ) ) {
      $rand_bytes .= substr( $bag, $random, 1 );
  }

  return $rand_bytes;
}

sub shuffle {
    my($self, $aref) = @_;
    croak 'Argument must be an array reference.' unless 'ARRAY' eq ref $aref;
    return $aref unless @$aref;
    for (my $i = @$aref; --$i;) {
        my $r = ($self->_ranged_randoms($i+1, 1))[0];
        ($aref->[$i],$aref->[$r]) = ($aref->[$r], $aref->[$i]);
    }
    return $aref;
}

# Helpers for string_from() and shuffle.

sub _ranged_randoms {
    my ( $self, $range, $count ) = @_;
    $count = defined $count ? $count : 0;

    # Lazily seed the RNG so we don't waste available strong entropy.
    $self->_instantiate_rng unless defined $self->{_RNG};

    my $divisor = $self->_closest_divisor($range);

    my @randoms;

    $#randoms = $count - 1;  # Pre-extend the @randoms array so 'push' avoids
                             # copy on resize.
    @randoms = ();           # Then purge it, but its memory won't be released.

    for my $n ( 1 .. $count ) {
        my $random;

        # The loop rolls, and re-rolls if the random number is out of the bag's
        # range.  This is to avoid a solution that would introduce modulo bias.
        do {
            $random = $self->{_RNG}->irand % $divisor;
        } while ( $random >= $range );

        push @randoms, $random;
    }

    return @randoms;
}

# Find nearest factor of 2**32 >= $range.

sub _closest_divisor {
    my ( $self, $range ) = @_;
    $range = defined $range ? $range : 0;

    croak "$range must be positive." if $range < 0;
    croak "$range exceeds irand max limit of 2**32." if $range > 2**32;

    my $n = 0;
    my $d;
    while ( $n <= 32 ) {
        $d = 2 ** $n++;
        last if $d >= $range;
    }

     return $d;
}

# irand, so that people who don't need "bytes" can enjoy B::R::S's convenience
# without jumping through "unpack" hoops. (A suggestion from Dana Jacobsen.)

sub irand {
  my( $self ) = @_;
  $self->_instantiate_rng unless defined $self->{_RNG};
  return $self->{_RNG}->irand;
}

################################################################################
##  Functions interface                                                       ##
################################################################################

# Instantiate our random number generator(s) inside of a lexical closure,
# limiting the scope of the RNG object so it can't be tampered with.

{
  my $RNG_object = undef;

  # Lazily, instantiate the RNG object, but only once.
  my $fetch_RNG = sub {
    $RNG_object = Bytes::Random::Secure->new( FUNC_STD )
      unless defined $RNG_object;
    return $RNG_object;
  };

  sub random_bytes {
    return $fetch_RNG->()->bytes( @_ );
  }

  sub random_string_from {
    return $fetch_RNG->()->string_from( @_ );
  }

}

# Base64 encoded random bytes functions

sub random_bytes_base64 {
  my ( $bytes, $eof ) = @_;
  return encode_base64 random_bytes($bytes), defined($eof) ? $eof : qq{\n};
}

```

[Assistant]
diff --git a/lib/Bytes/Random/Secure.pm b/lib/Bytes/Random/Secure.pm
index 65fbfe2..5bd3714 100644
--- a/lib/Bytes/Random/Secure.pm
+++ b/lib/Bytes/Random/Secure.pm
@@ -156,6 +156,7 @@ sub _build_attributes {
       $self->{$arg} = exists $args->{$arg} ? $args->{$arg} : $default;
     }

+    $self->{_pid} = $$;
     $self->{_RNG} = undef;    # Lazy initialization.
     return $self;
 }
@@ -171,6 +172,8 @@ sub _instantiate_rng {
     my @seeds = $self->_generate_seed( %seed_opts );
     $self->{_RNG} = Math::Random::ISAAC->new(@seeds);

+    $self->{_pid} = $$;
+
     return $self->{_RNG};
 }

@@ -224,7 +227,7 @@ sub bytes {
   $bytes = defined $bytes ? $bytes : 0; # Default to zero bytes.
   $self->_validate_int( $bytes ); # Throws on violation.

-  $self->_instantiate_rng unless defined $self->{_RNG};
+  $self->_instantiate_rng unless $$ == $self->{_pid} && defined $self->{_RNG};

   my $str = '';

@@ -302,7 +305,7 @@ sub _ranged_randoms {
     $count = defined $count ? $count : 0;

     # Lazily seed the RNG so we don't waste available strong entropy.
-    $self->_instantiate_rng unless defined $self->{_RNG};
+    $self->_instantiate_rng unless $$ == $self->{_pid} && defined $self->{_RNG};

     my $divisor = $self->_closest_divisor($range);

@@ -354,7 +357,7 @@ sub _closest_divisor {

 sub irand {
   my( $self ) = @_;
-  $self->_instantiate_rng unless defined $self->{_RNG};
+  $self->_instantiate_rng unless $$ == $self->{_pid} && defined $self->{_RNG};
   return $self->{_RNG}->irand;
 }


[System]
You are a security engineer fixing a CVE. You are given the vulnerability description (symptom only) and the vulnerable source window. Author the MINIMAL fix as a unified diff. Follow these rules exactly:
1. Target ONLY the file named in the `# Vulnerable source (...)` header — do not invent or substitute file paths.
2. Modify ONLY code visible in the shown source window.
3. Change as few lines as possible; do not rewrite surrounding logic.
4. Output only the unified diff with no other text.
5. Prefer the minimal structural change: if an existing condition is missing a check, add it inline to that condition rather than inserting a new code block. If existing code is redundant or overcomplicated, replace it — do not add more code around it.
6. Edit the most specific method or class that directly handles the vulnerable operation — not a base class, interface, or Protocol definition.

[User]
# CVE
CVE-2026-2245: An attacker can trigger memory corruption or code execution by providing a crafted transport stream file.

# Vulnerable source (src/lib_ccx/ts_tables.c)
```c
					for (ser_i = 0; ser_i < nb_service; ser_i++)
					{
						dbg_print(CCX_DMT_PMT, "CC SERVICE %d: language (%c%c%c)", nb_service,
							  es_info[1], es_info[2], es_info[3]);
						is_608 = es_info[4] >> 7;
						dbg_print(CCX_DMT_PMT, "%s", is_608 ? " CEA-608" : " CEA-708");
						dbg_print(CCX_DMT_PMT, "%s", is_608 ? " CEA-608" : " CEA-708");
					}
				}
				update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_ATSC_CC, program_number, NULL);
			}
		}

		if (IS_FEASIBLE(ctx->codec, ctx->nocodec, CCX_CODEC_TELETEXT) && ES_info_length && stream_type == CCX_STREAM_TYPE_PRIVATE_MPEG2) // MPEG-2 Packetized Elementary Stream packets containing private data
		{
			unsigned char *es_info = buf + i + 5;
			for (desc_len = 0; (buf + i + 5 + ES_info_length) - es_info; es_info += desc_len)
			{
				enum ccx_mpeg_descriptor descriptor_tag = (enum ccx_mpeg_descriptor)(*es_info++);
				desc_len = (*es_info++);
				if (!IS_VALID_TELETEXT_DESC(descriptor_tag))
					continue;
				update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_TELETEXT, program_number, NULL);
				mprint("VBI/teletext stream ID %u (0x%x) for SID %u (0x%x)\n",
				       elementary_PID, elementary_PID, program_number, program_number);
			}
		}

		if (!IS_FEASIBLE(ctx->codec, ctx->nocodec, CCX_CODEC_TELETEXT) &&
		    stream_type == CCX_STREAM_TYPE_PRIVATE_MPEG2) // MPEG-2 Packetized Elementary Stream packets containing private data
		{
			unsigned descriptor_tag = buf[i + 5];
			if (descriptor_tag == 0x45)
			{
				update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_ATSC_CC, program_number, NULL);
				// mprint ("VBI stream ID %u (0x%x) for SID %u (0x%x) - teletext is disabled, will be processed as closed captions.\n",
				//		elementary_PID, elementary_PID, program_number, program_number);
			}
		}

		if (stream_type == CCX_STREAM_TYPE_VIDEO_H264 || stream_type == CCX_STREAM_TYPE_VIDEO_MPEG2)
		{
			update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_ATSC_CC, program_number, NULL);
			// mprint ("Decode captions from program %d - %s stream [0x%02x]  -  PID: %u\n",
			//	program_number , desc[stream_type], stream_type, elementary_PID);
		}

		if (need_cap_info_for_pid(ctx, elementary_PID) == CCX_TRUE)
		{
			// We found the user selected CAPPID in PMT. We make a note of its type and don't
			// touch anything else
			if (stream_type >= 0x80 && stream_type <= 0xFF)
			{
				mprint("I can't tell the stream type of the manually selected PID.\n");
				mprint("Please pass -streamtype to select manually.\n");
				fatal(EXIT_FAILURE, "-streamtype has to be manually selected.");
			}
			update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_NONE, program_number, NULL);
			continue;
		}

		// For the print command below
		unsigned tmp_stream_type = get_printable_stream_type(stream_type);
		dbg_print(CCX_DMT_VERBOSE, "  %s stream [0x%02x]  -  PID: %u\n",
			  desc[tmp_stream_type],
			  stream_type, elementary_PID);
		i += ES_info_length;
	}

	pinfo->analysed_PMT_once = CCX_TRUE;

	ret = verify_crc32(sbuf, olen);
	if (ret == CCX_FALSE)
		pinfo->valid_crc = CCX_FALSE;
	else
		pinfo->valid_crc = CCX_TRUE;

	pinfo->crc = (*(int32_t *)(sbuf + olen - 4));

	return must_flush;
}

void ts_buffer_psi_packet(struct ccx_demuxer *ctx)
{
	unsigned char *payload_start = tspacket + 4;
	unsigned payload_length = 188 - 4;
	//	unsigned transport_error_indicator = (tspacket[1]&0x80)>>7;
	unsigned payload_start_indicator = (tspacket[1] & 0x40) >> 6;
	// 	unsigned transport_priority = (tspacket[1]&0x20)>>5;
	unsigned pid = (((tspacket[1] & 0x1F) << 8) | tspacket[2]) & 0x1FFF;
	// 	unsigned transport_scrambling_control = (tspacket[3]&0xC0)>>6;
	unsigned adaptation_field_control = (tspacket[3] & 0x30) >> 4;
	unsigned ccounter = tspacket[3] & 0xF;
	unsigned adaptation_field_length = 0;

	if (adaptation_field_control & 2)
	{
		adaptation_field_length = tspacket[4];
		payload_start = payload_start + adaptation_field_length + 1;
		payload_length = tspacket + 188 - payload_start;
	}

	if (ctx->PID_buffers[pid] == NULL)
	{ // First packet for this pid. Create a buffer
		ctx->PID_buffers[pid] = malloc(sizeof(struct PSI_buffer));
		ctx->PID_buffers[pid]->buffer = NULL;
		ctx->PID_buffers[pid]->buffer_length = 0;
		ctx->PID_buffers[pid]->ccounter = 0;
		ctx->PID_buffers[pid]->prev_ccounter = 0xff;
	}

	// skip the packet if the adaptation field length or payload length are out of bounds or broken
	if (adaptation_field_length > 184 || payload_length > 184)
	{
		payload_length = 0;
		dbg_print(CCX_DMT_GENERIC_NOTICES, "\rWarning: Bad packet, adaptation field too long, skipping.\n");
	}

	if (payload_start_indicator)
	{
		if (ctx->PID_buffers[pid]->ccounter > 0)
		{
			ctx->PID_buffers[pid]->ccounter = 0;
		}
		ctx->PID_buffers[pid]->prev_ccounter = ccounter;

		if (ctx->PID_buffers[pid]->buffer != NULL)
			free(ctx->PID_buffers[pid]->buffer);
		else
		{
			// must be first packet for PID
		}
		ctx->PID_buffers[pid]->buffer = (uint8_t *)malloc(payload_length);
		memcpy(ctx->PID_buffers[pid]->buffer, payload_start, payload_length);
		ctx->PID_buffers[pid]->buffer_length = payload_length;
		ctx->PID_buffers[pid]->ccounter++;
	}
	else if (ccounter == ctx->PID_buffers[pid]->prev_ccounter + 1 || (ctx->PID_buffers[pid]->prev_ccounter == 0x0f && ccounter == 0))
	{
		ctx->PID_buffers[pid]->prev_ccounter = ccounter;
		ctx->PID_buffers[pid]->buffer = (uint8_t *)realloc(ctx->PID_buffers[pid]->buffer, ctx->PID_buffers[pid]->buffer_length + payload_length);
		memcpy(ctx->PID_buffers[pid]->buffer + ctx->PID_buffers[pid]->buffer_length, payload_start, payload_length);
		ctx->PID_buffers[pid]->ccounter++;
		ctx->PID_buffers[pid]->buffer_length += payload_length;
	}
	else if (ctx->PID_buffers[pid]->prev_ccounter <= 0x0f)
	{
		dbg_print(CCX_DMT_GENERIC_NOTICES, "\rWarning: Out of order packets detected for PID: %u.\n\
       ctx->PID_buffers[pid]->prev_ccounter:%" PRIu32 " ctx->ctx->PID_buffers[pid]->ccounter:%" PRIu32 "\n",
			  pid, ctx->PID_buffers[pid]->prev_ccounter, ctx->PID_buffers[pid]->ccounter);
	}
}

/* Program Allocation Table. It contains a list of all programs and the
   PIDs of their Program Map Table.
   Returns: gotpes */

int parse_PAT(struct ccx_demuxer *ctx)
{
	int gotpes = 0;
	int is_multiprogram = 0;
	unsigned char pointer_field = 0;
	unsigned char *payload_start = NULL;
	unsigned int payload_length = 0;
	unsigned int section_number = 0;
	unsigned int last_section_number = 0;

	pointer_field = *(ctx->PID_buffers[0]->buffer);

	payload_start = ctx->PID_buffers[0]->buffer + pointer_field + 1;
	payload_length = ctx->PID_buffers[0]->buffer_length - (pointer_field + 1);

	section_number = payload_start[6];
	last_section_number = payload_start[7];

	unsigned section_length = (((payload_start[1] & 0x0F) << 8) | payload_start[2]);
	payload_length = ctx->PID_buffers[0]->buffer_length - 8;
	unsigned programm_data = section_length - 5 - 4; // prev. bytes and CRC

	if (programm_data + 4 > payload_length)
	{
		return 0; // We don't have the full section yet. We will parse again when we have it.
	}

	if (section_number > last_section_number) // Impossible: Defective PAT
	{
		dbg_print(CCX_DMT_PAT, "Skipped defective PAT packet, section_number=%u but last_section_number=%u\n",
			  section_number, last_section_number);
		return gotpes;
	}
	if (last_section_number > 0)
	{
		dbg_print(CCX_DMT_PAT, "Long PAT packet (%u / %u), skipping.\n",
			  section_number, last_section_number);
		return gotpes;
		/* fatal(CCX_COMMON_EXIT_BUG_BUG,
		   "Sorry, long PATs not yet supported!\n"); */
	}

	if (ctx->last_pat_payload != NULL && payload_length == ctx->last_pat_length &&
	    !memcmp(payload_start, ctx->last_pat_payload, payload_length))
	{
		// dbg_print(CCX_DMT_PAT, "PAT hasn't changed, skipping.\n");
		return CCX_OK;
	}

	if (ctx->last_pat_payload != NULL)
	{
		mprint("Notice: PAT changed, clearing all variables.\n");
		dinit_cap(ctx);
		clear_PMT_array(ctx);
		memset(ctx->PIDs_seen, 0, sizeof(int) * 65536); // Forget all we saw
		if (!tlt_config.user_page)			// If the user didn't select a page...
			tlt_config.page = 0;			// ..forget whatever we detected.

		gotpes = 1;
	}

	if (ctx->last_pat_length < payload_length + 8)
	{
		ctx->last_pat_payload = (unsigned char *)realloc(ctx->last_pat_payload, payload_length + 8); // Extra 8 in case memcpy copies dwords, etc
		if (ctx->last_pat_payload == NULL)
		{
			fatal(EXIT_NOT_ENOUGH_MEMORY, "Not enough memory to process PAT.\n");
			return -1;
		}
	}
	memcpy(ctx->last_pat_payload, payload_start, payload_length);
	ctx->last_pat_length = payload_length;

	unsigned table_id = payload_start[0];
	unsigned transport_stream_id = ((payload_start[3] << 8) | payload_start[4]);
	unsigned version_number = (payload_start[5] & 0x3E) >> 1;

	// Means current OR next (so you can build a long PAT before it
	// actually is to be in use).
	unsigned current_next_indicator = payload_start[5] & 0x01;

	if (!current_next_indicator)
		// This table is not active, no need to evaluate
		return 0;

	payload_start += 8;

	dbg_print(CCX_DMT_PAT, "Read PAT packet (id: %u) ts-id: 0x%04x\n",
		  table_id, transport_stream_id);
	dbg_print(CCX_DMT_PAT, "  section length: %u  number: %u  last: %u\n",
		  section_length, section_number, last_section_number);
	dbg_print(CCX_DMT_PAT, "  version_number: %u  current_next_indicator: %u\n",
		  version_number, current_next_indicator);

	dbg_print(CCX_DMT_PAT, "\nProgram association section (PAT)\n");

	ctx->freport.program_cnt = 0;
	for (unsigned i = 0; i < programm_data; i += 4)
	{
		unsigned program_number = ((payload_start[i] << 8) | payload_start[i + 1]);
		if (!program_number)
			continue;
		ctx->freport.program_cnt++;
	}

	is_multiprogram = (ctx->freport.program_cnt > 1);

	for (unsigned int i = 0; i < programm_data; i += 4)
	{
		unsigned program_number = ((payload_start[i] << 8) | payload_start[i + 1]);
		unsigned prog_map_pid = ((payload_start[i + 2] << 8) | payload_start[i + 3]) & 0x1FFF;
		int j = 0;

		dbg_print(CCX_DMT_PAT, "  Program number: %u  -> PMTPID: %u\n",
			  program_number, prog_map_pid);

		if (!program_number)
			continue;

		/**
		 * loop never break at j == ctx->nb_program when program_number
		 * is already there in pinfo array and if we have program number
		 * already in our array we don't need to update our array
		 * so we break if program_number already exist and make j != ctx->nb_program
		 *
		 * Loop without break means j would be equal to ctx->nb_program
		 */
		for (j = 0; j < ctx->nb_program; j++)
		{
			if (ctx->pinfo[j].program_number == program_number)
			{
				if (ctx->flag_ts_forced_pn == CCX_TRUE && ctx->pinfo[j].pid == CCX_UNKNOWN)
				{
					ctx->pinfo[j].pid = prog_map_pid;
					ctx->pinfo[j].analysed_PMT_once = CCX_FALSE;
				}
				break;
			}
		}
```

[Assistant]
diff --git a/src/lib_ccx/ts_tables.c b/src/lib_ccx/ts_tables.c
index 89611907c..7ff506cee 100644
--- a/src/lib_ccx/ts_tables.c
+++ b/src/lib_ccx/ts_tables.c
@@ -411,9 +411,18 @@ int parse_PMT(struct ccx_demuxer *ctx, unsigned char *buf, int len, struct progr
 		{
 			// if this any generally used video stream tyoe get clashed with ATSC/SCTE standard
 			// then this code can go in some atsc flag
+			// Validate ES_info_length against buffer bounds to prevent heap overflow
+			if (i + 5 + ES_info_length > len)
+				break;
+
 			unsigned char *es_info = buf + i + 5;
-			for (desc_len = 0; (buf + i + 5 + ES_info_length) > es_info; es_info += desc_len)
+			unsigned char *es_info_end = buf + i + 5 + ES_info_length;
+			for (desc_len = 0; es_info_end > es_info; es_info += desc_len)
 			{
+				// Need at least 2 bytes for descriptor_tag and desc_len
+				if (es_info + 2 > es_info_end)
+					break;
+
 				enum ccx_mpeg_descriptor descriptor_tag = (enum ccx_mpeg_descriptor)(*es_info++);
 				int nb_service;
 				int is_608;
@@ -437,9 +446,18 @@ int parse_PMT(struct ccx_demuxer *ctx, unsigned char *buf, int len, struct progr

 		if (IS_FEASIBLE(ctx->codec, ctx->nocodec, CCX_CODEC_TELETEXT) && ES_info_length && stream_type == CCX_STREAM_TYPE_PRIVATE_MPEG2) // MPEG-2 Packetized Elementary Stream packets containing private data
 		{
+			// Validate ES_info_length against buffer bounds
+			if (i + 5 + ES_info_length > len)
+				continue;
+
 			unsigned char *es_info = buf + i + 5;
-			for (desc_len = 0; (buf + i + 5 + ES_info_length) - es_info; es_info += desc_len)
+			unsigned char *es_info_end = buf + i + 5 + ES_info_length;
+			for (desc_len = 0; es_info_end > es_info; es_info += desc_len)
 			{
+				// Need at least 2 bytes for descriptor_tag and desc_len
+				if (es_info + 2 > es_info_end)
+					break;
+
 				enum ccx_mpeg_descriptor descriptor_tag = (enum ccx_mpeg_descriptor)(*es_info++);
 				desc_len = (*es_info++);
 				if (!IS_VALID_TELETEXT_DESC(descriptor_tag))
@@ -621,6 +639,10 @@ int parse_PAT(struct ccx_demuxer *ctx)
 	payload_start = ctx->PID_buffers[0]->buffer + pointer_field + 1;
 	payload_length = ctx->PID_buffers[0]->buffer_length - (pointer_field + 1);

+	// Need at least 8 bytes to read header fields
+	if (payload_length < 8)
+		return 0;
+
 	section_number = payload_start[6];
 	last_section_number = payload_start[7];