Fix memory leak in streamGetEdgeID (#10753)

si is initialized by streamIteratorStart(), we should call
streamIteratorStop() on it when done.

regression introduced in #9127 (redis 7.0)

@@ -401,7 +401,7 @@ void streamGetEdgeID(stream *s, int first, int skip_tombstones, streamID *edge_i
         streamID min_id = {0, 0}, max_id = {UINT64_MAX, UINT64_MAX};
         *edge_id = first ? max_id : min_id;
     }
-
+    streamIteratorStop(&si);
 }

 /* Adds a new item into the stream 's' having the specified number of


patch 8.2.5123: using invalid index when looking for spell suggestions

Problem:    Using invalid index when looking for spell suggestions.
Solution:   Do not decrement the index when it is zero.

@@ -1973,7 +1973,8 @@ suggest_trie_walk(
 			    sp->ts_isdiff = (newscore != 0)
 						       ? DIFF_YES : DIFF_NONE;
 			}
-			else if (sp->ts_isdiff == DIFF_INSERT)
+			else if (sp->ts_isdiff == DIFF_INSERT
+							    && sp->ts_fidx > 0)
 			    // When inserting trail bytes don't advance in the
 			    // bad word.
 			    --sp->ts_fidx;


@@ -7861,6 +7861,8 @@ MagickExport int XMenuWidget(Display *display,XWindows *windows,
             break;
           }
         state&=(~InactiveWidgetState);
+        if (selection_info.height == 0)
+          break;
         id=(event.xbutton.y-top_offset)/(int) selection_info.height;
         selection_info.id=id;
         if ((id < 0) || (id >= (int) number_selections))
@@ -7914,6 +7916,8 @@ MagickExport int XMenuWidget(Display *display,XWindows *windows,
         if (event.xcrossing.state == 0)
           break;
         state&=(~InactiveWidgetState);
+        if (selection_info.height == 0)
+          break;
         id=((event.xcrossing.y-top_offset)/(int) selection_info.height);
         if ((selection_info.id >= 0) &&
             (selection_info.id < (int) number_selections))
@@ -8000,6 +8004,8 @@ MagickExport int XMenuWidget(Display *display,XWindows *windows,
           break;
         if (state & InactiveWidgetState)
           break;
+        if (selection_info.height == 0)
+          break;
         id=(event.xmotion.y-top_offset)/(int) selection_info.height;
         if ((selection_info.id >= 0) &&
             (selection_info.id < (int) number_selections))


Fixes vulnerability DW202205-001
Reports error instead of crashing the when reading
a specially crafted (corrupted) .debug_pubnames
or .debug_pubtypes section.
	modified:   src/lib/libdwarf/dwarf_global.c

@@ -31,7 +31,6 @@
 #include <config.h>

 #include <string.h> /* strlen() */
-
 #if defined(_WIN32) && defined(HAVE_STDAFX_H)
 #include "stdafx.h"
 #endif /* HAVE_STDAFX_H */
@@ -83,6 +82,33 @@ _dwarf_fix_up_offset_irix(Dwarf_Debug dbg,
 }
 #endif /* __sgi */

+#if 0
+/*  Debugging only. Requires start. can calulate one of len, end */
+static void
+debug_print_range(const char *msg,
+    int lineno,
+    void *start, signed long len,
+    void *end)
+{
+
+    char *st = (char *)start;
+    char *en = (char *)end;
+    signed long le = len;
+
+    if (len) {
+        if (en) {
+            le = (long)(en-st);
+        } else {
+            en= start+len;
+        }
+    } else if (en) {
+        le = (long)(en-st);
+    }
+    printf("RANGEdebug %s  st=0x%lx le=%ld en=0x%lx line %d\n",
+        msg,(unsigned long)st,le,(unsigned long)en,lineno);
+}
+#endif
+
 static void
 dealloc_globals_chain(Dwarf_Debug dbg,
     Dwarf_Chain head_chain)
@@ -287,9 +313,9 @@ _dwarf_internal_get_pubnames_like_data(Dwarf_Debug dbg,
     Dwarf_Off pubnames_section_offset = 0;
     Dwarf_Small *section_end_ptr = section_data_ptr +section_length;

-    /*  Points to the context for the current set of global names, and
-        contains information to identify the compilation-unit that the
-        set refers to. */
+    /*  Points to the context for the current set of global names,
+        and contains information to identify the compilation-unit
+        that the set refers to. */
     Dwarf_Global_Context pubnames_context = 0;
     Dwarf_Bool           pubnames_context_on_list = FALSE;

@@ -301,6 +327,10 @@ _dwarf_internal_get_pubnames_like_data(Dwarf_Debug dbg,

     Dwarf_Unsigned global_count = 0;

+    /*  The count is just to improve the error message
+        a few lines above. */
+    Dwarf_Unsigned context_count = 0;
+
     /*  Used to chain the Dwarf_Global_s structs for
         creating contiguous list of pointers to the structs. */
     Dwarf_Chain head_chain = 0;
@@ -384,6 +414,41 @@ _dwarf_internal_get_pubnames_like_data(Dwarf_Debug dbg,
             }
             return mres;
         }
+        {
+            Dwarf_Small * localend =pubnames_like_ptr + length;
+
+            if ((length > section_length) ||
+                (localend > section_end_ptr)){
+                /*  The length field  is corrupted */
+                dwarfstring m;
+
+                dwarfstring_constructor(&m);
+                dwarfstring_append_printf_u(&m,
+                    "DW_DLE_PUBNAMES_LENGTH_BAD (or similar) "
+                    "A DWARF length field in cu context %u ",
+                    context_count);
+                dwarfstring_append_printf_s(&m,"of section %s ",
+                    (char *)secname);
+                dwarfstring_append_printf_u(&m,"of "
+                    "%u bytes ",length);
+                dwarfstring_append_printf_u(&m,
+                    "runs off the end of "
+                    "the %u bytes of the real section",
+                    section_length);
+                _dwarf_error_string(dbg, error,length_err_num,
+                    dwarfstring_string(&m));
+                dwarfstring_destructor(&m);
+                dealloc_globals_chain(dbg,head_chain);
+                if (!pubnames_context_on_list) {
+                    dwarf_dealloc(dbg,pubnames_context,
+                        context_DLA_code);
+                }
+                return DW_DLV_ERROR;
+            }
+        }
+        /*  The count is just to improve the error message
+            a few lines above. */
+        ++context_count;
         pubnames_context->pu_alloc_type = context_DLA_code;
         pubnames_context->pu_length_size = local_length_size;
         pubnames_context->pu_length = length;
@@ -393,8 +458,7 @@ _dwarf_internal_get_pubnames_like_data(Dwarf_Debug dbg,
         pubnames_ptr_past_end_cu = pubnames_like_ptr + length;
         pubnames_context->pu_pub_entries_end_ptr =
             pubnames_ptr_past_end_cu;
-
-        if ((pubnames_like_ptr + (DWARF_HALF_SIZE) ) >
+        if ((pubnames_like_ptr + (DWARF_HALF_SIZE) ) >=
             /* A minimum size needed */
             section_end_ptr) {
             pubnames_error_length(dbg,error,
@@ -554,6 +618,7 @@ _dwarf_internal_get_pubnames_like_data(Dwarf_Debug dbg,
         while (die_offset_in_cu) {
             int res = 0;
             unsigned char *glname = 0;
+            Dwarf_Unsigned nstrlen = 0;

             /*  non-zero die_offset_in_cu already read, so
                 pubnames_like_ptr points to a string.  */
@@ -570,8 +635,8 @@ _dwarf_internal_get_pubnames_like_data(Dwarf_Debug dbg,
                 return res;
             }
             glname = (unsigned char *)pubnames_like_ptr;
-            pubnames_like_ptr = pubnames_like_ptr +
-                strlen((char *) pubnames_like_ptr) + 1;
+            nstrlen = strlen((char *)pubnames_like_ptr);
+            pubnames_like_ptr += nstrlen + 1;
             /*  Already read offset and verified string, glname
                 now points to the string. */
             res = _dwarf_make_global_add_to_chain(dbg,


@@ -6,10 +6,11 @@ public interface Arm64Const {

 // ARM64 CPU

-   public static final int UC_CPU_AARCH64_A57 = 0;
-   public static final int UC_CPU_AARCH64_A53 = 1;
-   public static final int UC_CPU_AARCH64_A72 = 2;
-   public static final int UC_CPU_AARCH64_MAX = 3;
+   public static final int UC_CPU_ARM64_A57 = 0;
+   public static final int UC_CPU_ARM64_A53 = 1;
+   public static final int UC_CPU_ARM64_A72 = 2;
+   public static final int UC_CPU_ARM64_MAX = 3;
+   public static final int UC_CPU_ARM64_ENDING = 4;

 // ARM64 registers


@@ -36,6 +36,7 @@
 UC_CPU_ARM_PXA270C0 = 31
 UC_CPU_ARM_PXA270C5 = 32
 UC_CPU_ARM_MAX = 33
+UC_CPU_ARM_ENDING = 34

 # ARM registers


Fix crash when mapping a big memory and calling uc_close

@@ -0,0 +1,268 @@
+#ifndef QEMU_RCU_QUEUE_H
+#define QEMU_RCU_QUEUE_H
+
+/*
+ * rcu_queue.h
+ *
+ * RCU-friendly versions of the queue.h primitives.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Copyright (c) 2013 Mike D. Day, IBM Corporation.
+ *
+ * IBM's contributions to this file may be relicensed under LGPLv2 or later.
+ */
+
+#include "qemu/queue.h"
+#include "qemu/atomic.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * List access methods.
+ */
+#define QLIST_EMPTY_RCU(head) (atomic_read(&(head)->lh_first) == NULL)
+#define QLIST_FIRST_RCU(head) (atomic_rcu_read(&(head)->lh_first))
+#define QLIST_NEXT_RCU(elm, field) (atomic_rcu_read(&(elm)->field.le_next))
+
+/*
+ * List functions.
+ */
+
+
+/*
+ *  The difference between atomic_read/set and atomic_rcu_read/set
+ *  is in the including of a read/write memory barrier to the volatile
+ *  access. atomic_rcu_* macros include the memory barrier, the
+ *  plain atomic macros do not. Therefore, it should be correct to
+ *  issue a series of reads or writes to the same element using only
+ *  the atomic_* macro, until the last read or write, which should be
+ *  atomic_rcu_* to introduce a read or write memory barrier as
+ *  appropriate.
+ */
+
+/* Upon publication of the listelm->next value, list readers
+ * will see the new node when following next pointers from
+ * antecedent nodes, but may not see the new node when following
+ * prev pointers from subsequent nodes until after the RCU grace
+ * period expires.
+ * see linux/include/rculist.h __list_add_rcu(new, prev, next)
+ */
+#define QLIST_INSERT_AFTER_RCU(listelm, elm, field) do {    \
+    (elm)->field.le_next = (listelm)->field.le_next;        \
+    (elm)->field.le_prev = &(listelm)->field.le_next;       \
+    atomic_rcu_set(&(listelm)->field.le_next, (elm));       \
+    if ((elm)->field.le_next != NULL) {                     \
+       (elm)->field.le_next->field.le_prev =                \
+        &(elm)->field.le_next;                              \
+    }                                                       \
+} while (/*CONSTCOND*/0)
+
+/* Upon publication of the listelm->prev->next value, list
+ * readers will see the new element when following prev pointers
+ * from subsequent elements, but may not see the new element
+ * when following next pointers from antecedent elements
+ * until after the RCU grace period expires.
+ */
+#define QLIST_INSERT_BEFORE_RCU(listelm, elm, field) do {   \
+    (elm)->field.le_prev = (listelm)->field.le_prev;        \
+    (elm)->field.le_next = (listelm);                       \
+    atomic_rcu_set((listelm)->field.le_prev, (elm));        \
+    (listelm)->field.le_prev = &(elm)->field.le_next;       \
+} while (/*CONSTCOND*/0)
+
+/* Upon publication of the head->first value, list readers
+ * will see the new element when following the head, but may
+ * not see the new element when following prev pointers from
+ * subsequent elements until after the RCU grace period has
+ * expired.
+ */
+#define QLIST_INSERT_HEAD_RCU(head, elm, field) do {    \
+    (elm)->field.le_prev = &(head)->lh_first;           \
+    (elm)->field.le_next = (head)->lh_first;            \
+    atomic_rcu_set((&(head)->lh_first), (elm));         \
+    if ((elm)->field.le_next != NULL) {                 \
+       (elm)->field.le_next->field.le_prev =            \
+        &(elm)->field.le_next;                          \
+    }                                                   \
+} while (/*CONSTCOND*/0)
+
+
+/* prior to publication of the elm->prev->next value, some list
+ * readers may still see the removed element when following
+ * the antecedent's next pointer.
+ */
+#define QLIST_REMOVE_RCU(elm, field) do {           \
+    if ((elm)->field.le_next != NULL) {             \
+       (elm)->field.le_next->field.le_prev =        \
+        (elm)->field.le_prev;                       \
+    }                                               \
+    atomic_set((elm)->field.le_prev, (elm)->field.le_next); \
+} while (/*CONSTCOND*/0)
+
+/* List traversal must occur within an RCU critical section.  */
+#define QLIST_FOREACH_RCU(var, head, field)                 \
+        for ((var) = atomic_rcu_read(&(head)->lh_first);    \
+                (var);                                      \
+                (var) = atomic_rcu_read(&(var)->field.le_next))
+
+/* List traversal must occur within an RCU critical section.  */
+#define QLIST_FOREACH_SAFE_RCU(var, head, field, next_var)           \
+    for ((var) = (atomic_rcu_read(&(head)->lh_first));               \
+      (var) &&                                                       \
+          ((next_var) = atomic_rcu_read(&(var)->field.le_next), 1);  \
+           (var) = (next_var))
+
+/*
+ * RCU simple queue
+ */
+
+/* Simple queue access methods */
+#define QSIMPLEQ_EMPTY_RCU(head)      (atomic_read(&(head)->sqh_first) == NULL)
+#define QSIMPLEQ_FIRST_RCU(head)       atomic_rcu_read(&(head)->sqh_first)
+#define QSIMPLEQ_NEXT_RCU(elm, field)  atomic_rcu_read(&(elm)->field.sqe_next)
+
+/* Simple queue functions */
+#define QSIMPLEQ_INSERT_HEAD_RCU(head, elm, field) do {         \
+    (elm)->field.sqe_next = (head)->sqh_first;                  \
+    if ((elm)->field.sqe_next == NULL) {                        \
+        (head)->sqh_last = &(elm)->field.sqe_next;              \
+    }                                                           \
+    atomic_rcu_set(&(head)->sqh_first, (elm));                  \
+} while (/*CONSTCOND*/0)
+
+#define QSIMPLEQ_INSERT_TAIL_RCU(head, elm, field) do {    \
+    (elm)->field.sqe_next = NULL;                          \
+    atomic_rcu_set((head)->sqh_last, (elm));               \
+    (head)->sqh_last = &(elm)->field.sqe_next;             \
+} while (/*CONSTCOND*/0)
+
+#define QSIMPLEQ_INSERT_AFTER_RCU(head, listelm, elm, field) do {       \
+    (elm)->field.sqe_next = (listelm)->field.sqe_next;                  \
+    if ((elm)->field.sqe_next == NULL) {                                \
+        (head)->sqh_last = &(elm)->field.sqe_next;                      \
+    }                                                                   \
+    atomic_rcu_set(&(listelm)->field.sqe_next, (elm));                  \
+} while (/*CONSTCOND*/0)
+
+#define QSIMPLEQ_REMOVE_HEAD_RCU(head, field) do {                     \
+    atomic_set(&(head)->sqh_first, (head)->sqh_first->field.sqe_next); \
+    if ((head)->sqh_first == NULL) {                                   \
+        (head)->sqh_last = &(head)->sqh_first;                         \
+    }                                                                  \
+} while (/*CONSTCOND*/0)
+
+#define QSIMPLEQ_REMOVE_RCU(head, elm, type, field) do {            \
+    if ((head)->sqh_first == (elm)) {                               \
+        QSIMPLEQ_REMOVE_HEAD_RCU((head), field);                    \
+    } else {                                                        \
+        struct type *curr = (head)->sqh_first;                      \
+        while (curr->field.sqe_next != (elm)) {                     \
+            curr = curr->field.sqe_next;                            \
+        }                                                           \
+        atomic_set(&curr->field.sqe_next,                           \
+                   curr->field.sqe_next->field.sqe_next);           \
+        if (curr->field.sqe_next == NULL) {                         \
+            (head)->sqh_last = &(curr)->field.sqe_next;             \
+        }                                                           \
+    }                                                               \
+} while (/*CONSTCOND*/0)
+
+#define QSIMPLEQ_FOREACH_RCU(var, head, field)                          \
+    for ((var) = atomic_rcu_read(&(head)->sqh_first);                   \
+         (var);                                                         \
+         (var) = atomic_rcu_read(&(var)->field.sqe_next))
+
+#define QSIMPLEQ_FOREACH_SAFE_RCU(var, head, field, next)                \
+    for ((var) = atomic_rcu_read(&(head)->sqh_first);                    \
+         (var) && ((next) = atomic_rcu_read(&(var)->field.sqe_next), 1); \
+         (var) = (next))
+
+/*
+ * RCU tail queue
+ */
+
+/* Tail queue access methods */
+#define QTAILQ_EMPTY_RCU(head)      (atomic_read(&(head)->tqh_first) == NULL)
+#define QTAILQ_FIRST_RCU(head)       atomic_rcu_read(&(head)->tqh_first)
+#define QTAILQ_NEXT_RCU(elm, field)  atomic_rcu_read(&(elm)->field.tqe_next)
+
+/* Tail queue functions */
+#define QTAILQ_INSERT_HEAD_RCU(head, elm, field) do {                   \
+    (elm)->field.tqe_next = (head)->tqh_first;                          \
+    if ((elm)->field.tqe_next != NULL) {                                \
+        (head)->tqh_first->field.tqe_circ.tql_prev =                    \
+            &(elm)->field.tqe_circ;                                     \
+    } else {                                                            \
+        (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ;             \
+    }                                                                   \
+    atomic_rcu_set(&(head)->tqh_first, (elm));                          \
+    (elm)->field.tqe_circ.tql_prev = &(head)->tqh_circ;                 \
+} while (/*CONSTCOND*/0)
+
+#define QTAILQ_INSERT_TAIL_RCU(head, elm, field) do {                   \
+    (elm)->field.tqe_next = NULL;                                       \
+    (elm)->field.tqe_circ.tql_prev = (head)->tqh_circ.tql_prev;         \
+    atomic_rcu_set(&(head)->tqh_circ.tql_prev->tql_next, (elm));        \
+    (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ;                 \
+} while (/*CONSTCOND*/0)
+
+#define QTAILQ_INSERT_AFTER_RCU(head, listelm, elm, field) do {         \
+    (elm)->field.tqe_next = (listelm)->field.tqe_next;                  \
+    if ((elm)->field.tqe_next != NULL) {                                \
+        (elm)->field.tqe_next->field.tqe_circ.tql_prev =                \
+            &(elm)->field.tqe_circ;                                     \
+    } else {                                                            \
+        (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ;             \
+    }                                                                   \
+    atomic_rcu_set(&(listelm)->field.tqe_next, (elm));                  \
+    (elm)->field.tqe_circ.tql_prev = &(listelm)->field.tqe_circ;        \
+} while (/*CONSTCOND*/0)
+
+#define QTAILQ_INSERT_BEFORE_RCU(listelm, elm, field) do {                \
+    (elm)->field.tqe_circ.tql_prev = (listelm)->field.tqe_circ.tql_prev;  \
+    (elm)->field.tqe_next = (listelm);                                    \
+    atomic_rcu_set(&(listelm)->field.tqe_circ.tql_prev->tql_next, (elm)); \
+    (listelm)->field.tqe_circ.tql_prev = &(elm)->field.tqe_circ;          \
+} while (/*CONSTCOND*/0)
+
+#define QTAILQ_REMOVE_RCU(head, elm, field) do {                        \
+    if (((elm)->field.tqe_next) != NULL) {                              \
+        (elm)->field.tqe_next->field.tqe_circ.tql_prev =                \
+            (elm)->field.tqe_circ.tql_prev;                             \
+    } else {                                                            \
+        (head)->tqh_circ.tql_prev = (elm)->field.tqe_circ.tql_prev;     \
+    }                                                                   \
+    atomic_set(&(elm)->field.tqe_circ.tql_prev->tql_next, (elm)->field.tqe_next); \
+    (elm)->field.tqe_circ.tql_prev = NULL;                              \
+} while (/*CONSTCOND*/0)
+
+#define QTAILQ_FOREACH_RCU(var, head, field)                            \
+    for ((var) = atomic_rcu_read(&(head)->tqh_first);                   \
+         (var);                                                         \
+         (var) = atomic_rcu_read(&(var)->field.tqe_next))
+
+#define QTAILQ_FOREACH_SAFE_RCU(var, head, field, next)                  \
+    for ((var) = atomic_rcu_read(&(head)->tqh_first);                    \
+         (var) && ((next) = atomic_rcu_read(&(var)->field.tqe_next), 1); \
+         (var) = (next))
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* QEMU_RCU_QUEUE_H */


@@ -61,6 +61,9 @@ bool SQClass::NewSlot(SQSharedState *ss,const SQObjectPtr &key,const SQObjectPtr
         _defaultvalues[_member_idx(temp)].val = val;
         return true;
     }
+	if (_members->CountUsed() >= MEMBER_MAX_COUNT) {
+		return false;
+	}
     if(belongs_to_static_table) {
         SQInteger mmidx;
         if((sq_type(val) == OT_CLOSURE || sq_type(val) == OT_NATIVECLOSURE) &&


softmmu: Always initialize xlat in address_space_translate_for_iotlb

The bug is an uninitialized memory read, along the translate_fail
path, which results in garbage being read from iotlb_to_section,
which can lead to a crash in io_readx/io_writex.

The bug may be fixed by writing any value with zero
in ~TARGET_PAGE_MASK, so that the call to iotlb_to_section using
the xlat'ed address returns io_mem_unassigned, as desired by the
translate_fail path.

It is most useful to record the original physical page address,
which will eventually be logged by memory_region_access_valid
when the access is rejected by unassigned_mem_accepts.

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1065
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-Id: <20220621153829.366423-1-richard.henderson@linaro.org>

@@ -669,7 +669,7 @@ void tcg_iommu_init_notifier_list(CPUState *cpu)

 /* Called from RCU critical section */
 MemoryRegionSection *
-address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr,
+address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr orig_addr,
                                   hwaddr *xlat, hwaddr *plen,
                                   MemTxAttrs attrs, int *prot)
 {
@@ -678,6 +678,7 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr,
     IOMMUMemoryRegionClass *imrc;
     IOMMUTLBEntry iotlb;
     int iommu_idx;
+    hwaddr addr = orig_addr;
     AddressSpaceDispatch *d =
         qatomic_rcu_read(&cpu->cpu_ases[asidx].memory_dispatch);

@@ -722,6 +723,16 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr,
     return section;

 translate_fail:
+    /*
+     * We should be given a page-aligned address -- certainly
+     * tlb_set_page_with_attrs() does so.  The page offset of xlat
+     * is used to index sections[], and PHYS_SECTION_UNASSIGNED = 0.
+     * The page portion of xlat will be logged by memory_region_access_valid()
+     * when this memory access is rejected, so use the original untranslated
+     * physical address.
+     */
+    assert((orig_addr & ~TARGET_PAGE_MASK) == 0);
+    *xlat = orig_addr;
     return &d->map.sections[PHYS_SECTION_UNASSIGNED];
 }


ax25: Fix UAF bugs in ax25 timers

There are race conditions that may lead to UAF bugs in
ax25_heartbeat_expiry(), ax25_t1timer_expiry(), ax25_t2timer_expiry(),
ax25_t3timer_expiry() and ax25_idletimer_expiry(), when we call
ax25_release() to deallocate ax25_dev.

One of the UAF bugs caused by ax25_release() is shown below:

      (Thread 1)                    |      (Thread 2)
ax25_dev_device_up() //(1)          |
...                                 | ax25_kill_by_device()
ax25_bind()          //(2)          |
ax25_connect()                      | ...
 ax25_std_establish_data_link()     |
  ax25_start_t1timer()              | ax25_dev_device_down() //(3)
   mod_timer(&ax25->t1timer,..)     |
                                    | ax25_release()
   (wait a time)                    |  ...
                                    |  ax25_dev_put(ax25_dev) //(4)FREE
   ax25_t1timer_expiry()            |
    ax25->ax25_dev->values[..] //USE|  ...
     ...                            |

We increase the refcount of ax25_dev in position (1) and (2), and
decrease the refcount of ax25_dev in position (3) and (4).
The ax25_dev will be freed in position (4) and be used in
ax25_t1timer_expiry().

The fail log is shown below:
==============================================================

[  106.116942] BUG: KASAN: use-after-free in ax25_t1timer_expiry+0x1c/0x60
[  106.116942] Read of size 8 at addr ffff88800bda9028 by task swapper/0/0
[  106.116942] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 5.17.0-06123-g0905eec574
[  106.116942] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-14
[  106.116942] Call Trace:
...
[  106.116942]  ax25_t1timer_expiry+0x1c/0x60
[  106.116942]  call_timer_fn+0x122/0x3d0
[  106.116942]  __run_timers.part.0+0x3f6/0x520
[  106.116942]  run_timer_softirq+0x4f/0xb0
[  106.116942]  __do_softirq+0x1c2/0x651
...

This patch adds del_timer_sync() in ax25_release(), which could ensure
that all timers stop before we deallocate ax25_dev.

Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

@@ -1053,6 +1053,11 @@ static int ax25_release(struct socket *sock)
 		ax25_destroy_socket(ax25);
 	}
 	if (ax25_dev) {
+		del_timer_sync(&ax25->timer);
+		del_timer_sync(&ax25->t1timer);
+		del_timer_sync(&ax25->t2timer);
+		del_timer_sync(&ax25->t3timer);
+		del_timer_sync(&ax25->idletimer);
 		dev_put_track(ax25_dev->dev, &ax25_dev->dev_tracker);
 		ax25_dev_put(ax25_dev);
 	}


Fix jpeg_skip_scanlines() segfault w/merged upsamp

The additional segfault mentioned in #244 was due to the fact that
the merged upsamplers use a different private structure than the
non-merged upsamplers.  jpeg_skip_scanlines() was assuming the latter, so
when merged upsampling was enabled, jpeg_skip_scanlines() clobbered one
of the IDCT method pointers in the merged upsampler's private structure.

For reasons unknown, the test image in #441 did not encounter this
segfault (too small?), but it encountered an issue similar to the one
fixed in 5bc43c7821df982f65aa1c738f67fbf7cba8bd69, whereby it was
necessary to set up a dummy postprocessing function in
read_and_discard_scanlines() when merged upsampling was enabled.
Failing to do so caused either a segfault in merged_2v_upsample() (due
to a NULL pointer being passed to jcopy_sample_rows()) or an error
("Corrupt JPEG data: premature end of data segment"), depending on the
number of scanlines skipped and whether the first scanline skipped was
an odd- or even-numbered row.

Fixes #441
Fixes #244 (for real this time)

@@ -4,7 +4,7 @@
  * This file was part of the Independent JPEG Group's software:
  * Copyright (C) 1994-1996, Thomas G. Lane.
  * libjpeg-turbo Modifications:
- * Copyright (C) 2010, 2015-2018, D. R. Commander.
+ * Copyright (C) 2010, 2015-2018, 2020, D. R. Commander.
  * Copyright (C) 2015, Google, Inc.
  * For conditions of distribution and use, see the accompanying README.ijg
  * file.
@@ -21,6 +21,8 @@
 #include "jinclude.h"
 #include "jdmainct.h"
 #include "jdcoefct.h"
+#include "jdmaster.h"
+#include "jdmerge.h"
 #include "jdsample.h"
 #include "jmemsys.h"

@@ -304,6 +306,16 @@ noop_quantize(j_decompress_ptr cinfo, JSAMPARRAY input_buf,
 }

+/* Dummy postprocessing function used by jpeg_skip_scanlines() */
+LOCAL(void)
+noop_post_process (j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
+                   JDIMENSION *in_row_group_ctr,
+                   JDIMENSION in_row_groups_avail, JSAMPARRAY output_buf,
+                   JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)
+{
+}
+
+
 /*
  * In some cases, it is best to call jpeg_read_scanlines() and discard the
  * output, rather than skipping the scanlines, because this allows us to
@@ -316,11 +328,17 @@ LOCAL(void)
 read_and_discard_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)
 {
   JDIMENSION n;
+  my_master_ptr master = (my_master_ptr)cinfo->master;
   void (*color_convert) (j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
                          JDIMENSION input_row, JSAMPARRAY output_buf,
                          int num_rows) = NULL;
   void (*color_quantize) (j_decompress_ptr cinfo, JSAMPARRAY input_buf,
                           JSAMPARRAY output_buf, int num_rows) = NULL;
+  void (*post_process_data) (j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
+                             JDIMENSION *in_row_group_ctr,
+                             JDIMENSION in_row_groups_avail,
+                             JSAMPARRAY output_buf, JDIMENSION *out_row_ctr,
+                             JDIMENSION out_rows_avail) = NULL;

   if (cinfo->cconvert && cinfo->cconvert->color_convert) {
     color_convert = cinfo->cconvert->color_convert;
@@ -332,6 +350,12 @@ read_and_discard_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)
     cinfo->cquantize->color_quantize = noop_quantize;
   }

+  if (master->using_merged_upsample && cinfo->post &&
+      cinfo->post->post_process_data) {
+    post_process_data = cinfo->post->post_process_data;
+    cinfo->post->post_process_data = noop_post_process;
+  }
+
   for (n = 0; n < num_lines; n++)
     jpeg_read_scanlines(cinfo, NULL, 1);

@@ -340,6 +364,9 @@ read_and_discard_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)

   if (color_quantize)
     cinfo->cquantize->color_quantize = color_quantize;
+
+  if (post_process_data)
+    cinfo->post->post_process_data = post_process_data;
 }

@@ -382,7 +409,7 @@ jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)
 {
   my_main_ptr main_ptr = (my_main_ptr)cinfo->main;
   my_coef_ptr coef = (my_coef_ptr)cinfo->coef;
-  my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample;
+  my_master_ptr master = (my_master_ptr)cinfo->master;
   JDIMENSION i, x;
   int y;
   JDIMENSION lines_per_iMCU_row, lines_left_in_iMCU_row, lines_after_iMCU_row;
@@ -445,8 +472,16 @@ jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)
     main_ptr->buffer_full = FALSE;
     main_ptr->rowgroup_ctr = 0;
     main_ptr->context_state = CTX_PREPARE_FOR_IMCU;
-    upsample->next_row_out = cinfo->max_v_samp_factor;
-    upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
+    if (master->using_merged_upsample) {
+      my_merged_upsample_ptr upsample =
+        (my_merged_upsample_ptr)cinfo->upsample;
+      upsample->spare_full = FALSE;
+      upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
+    } else {
+      my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample;
+      upsample->next_row_out = cinfo->max_v_samp_factor;
+      upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
+    }
   }

   /* Skipping is much simpler when context rows are not required. */
@@ -458,8 +493,16 @@ jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)
       cinfo->output_scanline += lines_left_in_iMCU_row;
       main_ptr->buffer_full = FALSE;
       main_ptr->rowgroup_ctr = 0;
-      upsample->next_row_out = cinfo->max_v_samp_factor;
-      upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
+      if (master->using_merged_upsample) {
+        my_merged_upsample_ptr upsample =
+          (my_merged_upsample_ptr)cinfo->upsample;
+        upsample->spare_full = FALSE;
+        upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
+      } else {
+        my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample;
+        upsample->next_row_out = cinfo->max_v_samp_factor;
+        upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
+      }
     }
   }

@@ -494,7 +537,14 @@ jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)
       cinfo->output_iMCU_row += lines_to_skip / lines_per_iMCU_row;
       increment_simple_rowgroup_ctr(cinfo, lines_to_read);
     }
-    upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
+    if (master->using_merged_upsample) {
+      my_merged_upsample_ptr upsample =
+        (my_merged_upsample_ptr)cinfo->upsample;
+      upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
+    } else {
+      my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample;
+      upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
+    }
     return num_lines;
   }

@@ -535,7 +585,13 @@ jpeg_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines)
    * bit odd, since "rows_to_go" seems to be redundantly keeping track of
    * output_scanline.
    */
-  upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
+  if (master->using_merged_upsample) {
+    my_merged_upsample_ptr upsample = (my_merged_upsample_ptr)cinfo->upsample;
+    upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
+  } else {
+    my_upsample_ptr upsample = (my_upsample_ptr)cinfo->upsample;
+    upsample->rows_to_go = cinfo->output_height - cinfo->output_scanline;
+  }

   /* Always skip the requested number of lines. */
   return num_lines;


ax25: Fix NULL pointer dereference in ax25_kill_by_device

When two ax25 devices attempted to establish connection, the requester use ax25_create(),
ax25_bind() and ax25_connect() to initiate connection. The receiver use ax25_rcv() to
accept connection and use ax25_create_cb() in ax25_rcv() to create ax25_cb, but the
ax25_cb->sk is NULL. When the receiver is detaching, a NULL pointer dereference bug
caused by sock_hold(sk) in ax25_kill_by_device() will happen. The corresponding
fail log is shown below:

===============================================================
BUG: KASAN: null-ptr-deref in ax25_device_event+0xfd/0x290
Call Trace:
...
ax25_device_event+0xfd/0x290
raw_notifier_call_chain+0x5e/0x70
dev_close_many+0x174/0x220
unregister_netdevice_many+0x1f7/0xa60
unregister_netdevice_queue+0x12f/0x170
unregister_netdev+0x13/0x20
mkiss_close+0xcd/0x140
tty_ldisc_release+0xc0/0x220
tty_release_struct+0x17/0xa0
tty_release+0x62d/0x670
...

This patch add condition check in ax25_kill_by_device(). If s->sk is
NULL, it will goto if branch to kill device.

Fixes: 4e0f718daf97 ("ax25: improve the incomplete fix to avoid UAF and NPD bugs")
Reported-by: Thomas Osterried <thomas@osterried.de>
Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
Signed-off-by: David S. Miller <davem@davemloft.net>

@@ -87,6 +87,13 @@ static void ax25_kill_by_device(struct net_device *dev)
 	ax25_for_each(s, &ax25_list) {
 		if (s->ax25_dev == ax25_dev) {
 			sk = s->sk;
+			if (!sk) {
+				spin_unlock_bh(&ax25_list_lock);
+				s->ax25_dev = NULL;
+				ax25_disconnect(s, ENETUNREACH);
+				spin_lock_bh(&ax25_list_lock);
+				goto again;
+			}
 			sock_hold(sk);
 			spin_unlock_bh(&ax25_list_lock);
 			lock_sock(sk);


@@ -773,7 +773,7 @@ static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
               break;
             for (x=0; x < (ssize_t) picon->columns; x++)
             {
-              if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
+              if (GetPixelAlpha(picon,q) == (Quantum) TransparentAlpha)
                 transparent=MagickTrue;
               else
                 SetPixelAlpha(picon,OpaqueAlpha,q);
@@ -804,7 +804,7 @@ static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
           break;
         for (x=0; x < (ssize_t) picon->columns; x++)
         {
-          if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
+          if (GetPixelAlpha(picon,q) == (Quantum) TransparentAlpha)
             SetPixelIndex(picon,(Quantum) picon->colors,q);
           q+=GetPixelChannels(picon);
         }


Bug 704834: Fix division by zero for zero width pages in muraster.

@@ -1003,8 +1003,9 @@ initialise_banding(fz_context *ctx, render_details *render, int color)

 	w = render->ibounds.x1 - render->ibounds.x0;
 	min_band_mem = (size_t)bpp * w * min_band_height;
-	reps = (int)(max_band_memory / min_band_mem);
-	if (reps < 1)
+	if (min_band_mem > 0)
+		reps = (int)(max_band_memory / min_band_mem);
+	if (min_band_mem == 0 || reps < 1)
 		reps = 1;

 	/* Adjust reps to even out the work between threads */


[JSC] Make JSMap and JSSet construction more simple and efficient
https://bugs.webkit.org/show_bug.cgi?id=243557
rdar://98068082

Reviewed by Mark Lam and Saam Barati.

This patch makes the initial buffer of JSMap / JSSet nullptr so that we can make allocation of them
simpler and efficient for non-using case. It cleans up many code in module loader etc. And it paves
the way to allocating them from DFG and FTL efficiently. It also cleans up SerializedScriptValue
implementation.

* JSTests/stress/map-clear-get.js: Added.
(shouldBe):
(test):
* JSTests/stress/set-clear-has.js: Added.
(shouldBe):
(set clear):
(set shouldBe):
(set new):
* Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileCompareStrictEq):
* Source/JavaScriptCore/runtime/AbstractModuleRecord.cpp:
(JSC::AbstractModuleRecord::finishCreation):
* Source/JavaScriptCore/runtime/HashMapImpl.h:
(JSC::HashMapBuffer::tryCreate):
(JSC::HashMapImpl::HashMapImpl):
(JSC::HashMapBuffer::create): Deleted.
(JSC::HashMapImpl::shouldRehashAfterAdd const): Deleted.
* Source/JavaScriptCore/runtime/HashMapImplInlines.h:
(JSC::shouldShrink):
(JSC::shouldRehash):
(JSC::nextCapacity):
(JSC::HashMapImpl<HashMapBucketType>::finishCreation):
(JSC::HashMapImpl<HashMapBucketType>::add):
(JSC::HashMapImpl<HashMapBucketType>::addNormalized):
(JSC::HashMapImpl<HashMapBucketType>::remove):
(JSC::HashMapImpl<HashMapBucketType>::clear):
(JSC::HashMapImpl<HashMapBucketType>::setUpHeadAndTail):
(JSC::HashMapImpl<HashMapBucketType>::addNormalizedNonExistingForCloning):
(JSC::HashMapImpl<HashMapBucketType>::addNormalizedNonExistingForCloningInternal):
(JSC::HashMapImpl<HashMapBucketType>::addNormalizedInternal):
(JSC::HashMapImpl<HashMapBucketType>::findBucketAlreadyHashedAndNormalized):
(JSC::HashMapImpl<HashMapBucketType>::rehash):
(JSC::HashMapImpl<HashMapBucketType>::makeAndSetNewBuffer):
(JSC::HashMapImpl<HashMapBucketType>::assertBufferIsEmpty):
(JSC::shouldRehashAfterAdd): Deleted.
(JSC::HashMapImpl<HashMapBucketType>::assertBufferIsEmpty const): Deleted.
* Source/JavaScriptCore/runtime/JSMap.h:
* Source/JavaScriptCore/runtime/JSModuleLoader.cpp:
(JSC::JSModuleLoader::finishCreation):
* Source/JavaScriptCore/runtime/JSSet.h:
* Source/JavaScriptCore/runtime/MapConstructor.cpp:
(JSC::JSC_DEFINE_HOST_FUNCTION):
* Source/JavaScriptCore/runtime/MapPrototype.cpp:
(JSC::JSC_DEFINE_HOST_FUNCTION):
* Source/JavaScriptCore/runtime/SetConstructor.cpp:
(JSC::JSC_DEFINE_HOST_FUNCTION):
* Source/JavaScriptCore/runtime/SetPrototype.cpp:
(JSC::JSC_DEFINE_HOST_FUNCTION):
* Source/JavaScriptCore/runtime/WeakMapImplInlines.h:
(JSC::WeakMapImpl<WeakMapBucket>::shouldRehashAfterAdd const):
* Source/WebCore/bindings/js/JSDOMMapLike.cpp:
(WebCore::getBackingMap):
* Source/WebCore/bindings/js/JSDOMSetLike.cpp:
(WebCore::getBackingSet):
* Source/WebCore/bindings/js/SerializedScriptValue.cpp:
(WebCore::CloneDeserializer::deserialize):

Canonical link: https://commits.webkit.org/253133@main

@@ -137,20 +137,25 @@ ALWAYS_INLINE std::optional<uint32_t> concurrentJSMapHash(JSValue key)
     return wangsInt64Hash(rawValue);
 }

+static constexpr uint32_t hashMapInitialCapacity = 4;
+
 ALWAYS_INLINE uint32_t shouldShrink(uint32_t capacity, uint32_t keyCount)
 {
-    return 8 * keyCount <= capacity && capacity > 4;
+    return 8 * keyCount <= capacity && capacity > hashMapInitialCapacity;
 }

-ALWAYS_INLINE uint32_t shouldRehashAfterAdd(uint32_t capacity, uint32_t keyCount, uint32_t deleteCount)
+ALWAYS_INLINE uint32_t shouldRehash(uint32_t capacity, uint32_t keyCount, uint32_t deleteCount)
 {
     return 2 * (keyCount + deleteCount) >= capacity;
 }

 ALWAYS_INLINE uint32_t nextCapacity(uint32_t capacity, uint32_t keyCount)
 {
+    if (!capacity)
+        return hashMapInitialCapacity;
+
     if (shouldShrink(capacity, keyCount)) {
-        ASSERT((capacity / 2) >= 4);
+        ASSERT((capacity / 2) >= hashMapInitialCapacity);
         return capacity / 2;
     }

@@ -173,17 +178,11 @@ ALWAYS_INLINE uint32_t nextCapacity(uint32_t capacity, uint32_t keyCount)
 }

 template <typename HashMapBucketType>
-void HashMapImpl<HashMapBucketType>::finishCreation(JSGlobalObject* globalObject, VM& vm)
+void HashMapImpl<HashMapBucketType>::finishCreation(VM& vm)
 {
     ASSERT_WITH_MESSAGE(HashMapBucket<HashMapBucketDataKey>::offsetOfKey() == HashMapBucket<HashMapBucketDataKeyValue>::offsetOfKey(), "We assume this to be true in the DFG and FTL JIT.");
-
-    auto scope = DECLARE_THROW_SCOPE(vm);
     Base::finishCreation(vm);
-
-    makeAndSetNewBuffer(globalObject, vm);
-    RETURN_IF_EXCEPTION(scope, void());
-
-    setUpHeadAndTail(globalObject, vm);
+    setUpHeadAndTail(vm);
 }

 template <typename HashMapBucketType>
@@ -195,12 +194,11 @@ void HashMapImpl<HashMapBucketType>::finishCreation(JSGlobalObject* globalObject
     // This size should be the same to the case when you clone the map by calling add() repeatedly.
     uint32_t capacity = (Checked<uint32_t>(base->m_keyCount) * 2) + 1;
     RELEASE_ASSERT(capacity <= (1U << 31));
-    capacity = std::max<uint32_t>(WTF::roundUpToPowerOfTwo(capacity), 4U);
-    m_capacity = capacity;
-    makeAndSetNewBuffer(globalObject, vm);
+    capacity = std::max<uint32_t>(WTF::roundUpToPowerOfTwo(capacity), hashMapInitialCapacity);
+    makeAndSetNewBuffer(globalObject, capacity, vm);
     RETURN_IF_EXCEPTION(scope, void());

-    setUpHeadAndTail(globalObject, vm);
+    setUpHeadAndTail(vm);

     HashMapBucketType* bucket = base->m_head.get()->next();
     while (bucket) {
@@ -249,17 +247,10 @@ ALWAYS_INLINE bool HashMapImpl<HashMapBucketType>::has(JSGlobalObject* globalObj
 template <typename HashMapBucketType>
 ALWAYS_INLINE void HashMapImpl<HashMapBucketType>::add(JSGlobalObject* globalObject, JSValue key, JSValue value)
 {
-    VM& vm = getVM(globalObject);
-    auto scope = DECLARE_THROW_SCOPE(vm);
-
     key = normalizeMapKey(key);
     addNormalizedInternal(globalObject, key, value, [&] (HashMapBucketType* bucket) {
         return !isDeleted(bucket) && areKeysEqual(globalObject, key, bucket->key());
     });
-    RETURN_IF_EXCEPTION(scope, void());
-    scope.release();
-    if (shouldRehashAfterAdd())
-        rehash(globalObject);
 }

 template <typename HashMapBucketType>
@@ -269,12 +260,9 @@ ALWAYS_INLINE HashMapBucketType* HashMapImpl<HashMapBucketType>::addNormalized(J
     ASSERT_WITH_MESSAGE(normalizeMapKey(key) == key, "We expect normalized values flowing into this function.");
     DEFER_TERMINATION_AND_ASSERT_WITH_MESSAGE(vm, jsMapHash(globalObject, getVM(globalObject), key) == hash, "We expect hash value is what we expect.");

-    auto* bucket = addNormalizedInternal(vm, key, value, hash, [&] (HashMapBucketType* bucket) {
+    return addNormalizedInternal(globalObject, key, value, hash, [&] (HashMapBucketType* bucket) {
         return !isDeleted(bucket) && areKeysEqual(globalObject, key, bucket->key());
     });
-    if (shouldRehashAfterAdd())
-        rehash(globalObject);
-    return bucket;
 }

 template <typename HashMapBucketType>
@@ -297,15 +285,14 @@ ALWAYS_INLINE bool HashMapImpl<HashMapBucketType>::remove(JSGlobalObject* global
     --m_keyCount;

     if (shouldShrink())
-        rehash(globalObject);
+        rehash(globalObject, RehashMode::AfterRemoval);

     return true;
 }

 template <typename HashMapBucketType>
-ALWAYS_INLINE void HashMapImpl<HashMapBucketType>::clear(JSGlobalObject* globalObject)
+ALWAYS_INLINE void HashMapImpl<HashMapBucketType>::clear(VM& vm)
 {
-    VM& vm = getVM(globalObject);
     m_keyCount = 0;
     m_deleteCount = 0;
     HashMapBucketType* head = m_head.get();
@@ -320,13 +307,13 @@ ALWAYS_INLINE void HashMapImpl<HashMapBucketType>::clear(JSGlobalObject* globalO
     }
     m_head->setNext(vm, m_tail.get());
     m_tail->setPrev(vm, m_head.get());
-    m_capacity = 4;
-    makeAndSetNewBuffer(globalObject, vm);
+    m_buffer.clear();
+    m_capacity = 0;
     checkConsistency();
 }

 template <typename HashMapBucketType>
-ALWAYS_INLINE void HashMapImpl<HashMapBucketType>::setUpHeadAndTail(JSGlobalObject*, VM& vm)
+ALWAYS_INLINE void HashMapImpl<HashMapBucketType>::setUpHeadAndTail(VM& vm)
 {
     m_head.set(vm, this, HashMapBucketType::create(vm));
     m_tail.set(vm, this, HashMapBucketType::create(vm));
@@ -340,7 +327,20 @@ ALWAYS_INLINE void HashMapImpl<HashMapBucketType>::setUpHeadAndTail(JSGlobalObje
 template <typename HashMapBucketType>
 ALWAYS_INLINE void HashMapImpl<HashMapBucketType>::addNormalizedNonExistingForCloning(JSGlobalObject* globalObject, JSValue key, JSValue value)
 {
-    addNormalizedInternal(globalObject, key, value, [&] (HashMapBucketType*) {
+    VM& vm = getVM(globalObject);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    uint32_t hash = jsMapHash(globalObject, vm, key);
+    RETURN_IF_EXCEPTION(scope, void());
+    scope.release();
+
+    addNormalizedNonExistingForCloningInternal(globalObject, key, value, hash);
+}
+
+template <typename HashMapBucketType>
+ALWAYS_INLINE HashMapBucketType* HashMapImpl<HashMapBucketType>::addNormalizedNonExistingForCloningInternal(JSGlobalObject* globalObject, JSValue key, JSValue value, uint32_t hash)
+{
+    return addNormalizedInternal(globalObject, key, value, hash, [&](HashMapBucketType*) {
         return false;
     });
 }
@@ -355,26 +355,49 @@ ALWAYS_INLINE void HashMapImpl<HashMapBucketType>::addNormalizedInternal(JSGloba
     uint32_t hash = jsMapHash(globalObject, vm, key);
     RETURN_IF_EXCEPTION(scope, void());
     scope.release();
-    addNormalizedInternal(vm, key, value, hash, canUseBucket);
+    addNormalizedInternal(globalObject, key, value, hash, canUseBucket);
 }

 template <typename HashMapBucketType>
 template<typename CanUseBucket>
-ALWAYS_INLINE HashMapBucketType* HashMapImpl<HashMapBucketType>::addNormalizedInternal(VM& vm, JSValue key, JSValue value, uint32_t hash, const CanUseBucket& canUseBucket)
+ALWAYS_INLINE HashMapBucketType* HashMapImpl<HashMapBucketType>::addNormalizedInternal(JSGlobalObject* globalObject, JSValue key, JSValue value, uint32_t hash, const CanUseBucket& canUseBucket)
 {
+    VM& vm = getVM(globalObject);
+    auto scope = DECLARE_THROW_SCOPE(vm);
     ASSERT_WITH_MESSAGE(normalizeMapKey(key) == key, "We expect normalized values flowing into this function.");

+    if (!m_capacity) {
+        makeAndSetNewBuffer(globalObject, hashMapInitialCapacity, vm);
+        RETURN_IF_EXCEPTION(scope, { });
+    }
+
     const uint32_t mask = m_capacity - 1;
     uint32_t index = hash & mask;
     HashMapBucketType** buffer = this->buffer();
-    HashMapBucketType* bucket = buffer[index];
-    while (!isEmpty(bucket)) {
-        if (canUseBucket(bucket)) {
-            bucket->setValue(vm, value);
-            return bucket;
+    {
+        HashMapBucketType* bucket = buffer[index];
+        while (!isEmpty(bucket)) {
+            if (canUseBucket(bucket)) {
+                bucket->setValue(vm, value);
+                return bucket;
+            }
+            index = (index + 1) & mask;
+            bucket = buffer[index];
         }
-        index = (index + 1) & mask;
-        bucket = buffer[index];
+    }
+
+    if (JSC::shouldRehash(m_capacity, m_keyCount + 1, m_deleteCount)) {
+        rehash(globalObject, RehashMode::BeforeAddition);
+        RETURN_IF_EXCEPTION(scope, { });
+        // We ensure that (1) this map does not have deleted keys because of rehashing and (2) this map does not have the same key as |key| input.
+        // Thus, we can just search for empty bucket.
+        ASSERT(m_capacity);
+        ASSERT(isPowerOfTwo(m_capacity));
+        const uint32_t mask = m_capacity - 1;
+        index = hash & mask;
+        buffer = this->buffer();
+        while (!isEmpty(buffer[index]))
+            index = (index + 1) & mask;
     }

     HashMapBucketType* newEntry = m_tail.get();
@@ -395,6 +418,9 @@ ALWAYS_INLINE HashMapBucketType* HashMapImpl<HashMapBucketType>::addNormalizedIn
 template <typename HashMapBucketType>
 ALWAYS_INLINE HashMapBucketType** HashMapImpl<HashMapBucketType>::findBucketAlreadyHashedAndNormalized(JSGlobalObject* globalObject, JSValue key, uint32_t hash)
 {
+    if (!m_capacity)
+        return nullptr;
+
     const uint32_t mask = m_capacity - 1;
     uint32_t index = hash & mask;
     HashMapBucketType** buffer = this->buffer();
@@ -410,20 +436,25 @@ ALWAYS_INLINE HashMapBucketType** HashMapImpl<HashMapBucketType>::findBucketAlre
 }

 template <typename HashMapBucketType>
-void HashMapImpl<HashMapBucketType>::rehash(JSGlobalObject* globalObject)
+void HashMapImpl<HashMapBucketType>::rehash(JSGlobalObject* globalObject, RehashMode mode)
 {
     VM& vm = getVM(globalObject);
     auto scope = DECLARE_THROW_SCOPE(vm);

     uint32_t oldCapacity = m_capacity;
-    m_capacity = nextCapacity(m_capacity, m_keyCount);
+    uint32_t newCapacity = nextCapacity(m_capacity, m_keyCount + (mode == RehashMode::BeforeAddition ? 1 : 0));
+    ASSERT(newCapacity);

-    if (m_capacity != oldCapacity) {
-        makeAndSetNewBuffer(globalObject, vm);
+    if (newCapacity != oldCapacity) {
+        makeAndSetNewBuffer(globalObject, newCapacity, vm);
         RETURN_IF_EXCEPTION(scope, void());
     } else {
-        m_buffer->reset(m_capacity);
-        assertBufferIsEmpty();
+        ASSERT(newCapacity);
+        ASSERT(oldCapacity);
+        ASSERT(m_capacity == newCapacity);
+        ASSERT(m_buffer);
+        m_buffer->reset(newCapacity);
+        assertBufferIsEmpty(buffer(), newCapacity);
     }

     HashMapBucketType* iter = m_head->next();
@@ -466,25 +497,28 @@ ALWAYS_INLINE void HashMapImpl<HashMapBucketType>::checkConsistency() const
 }

 template <typename HashMapBucketType>
-void HashMapImpl<HashMapBucketType>::makeAndSetNewBuffer(JSGlobalObject* globalObject, VM& vm)
+void HashMapImpl<HashMapBucketType>::makeAndSetNewBuffer(JSGlobalObject* globalObject, uint32_t newCapacity, VM& vm)
 {
-    ASSERT(!(m_capacity & (m_capacity - 1)));
+    ASSERT(!(newCapacity & (newCapacity - 1)));

-    HashMapBufferType* buffer = HashMapBufferType::create(globalObject, vm, this, m_capacity);
+    HashMapBufferType* buffer = HashMapBufferType::tryCreate(globalObject, vm, newCapacity);
     if (UNLIKELY(!buffer))
         return;

     m_buffer.set(vm, this, buffer);
-    assertBufferIsEmpty();
+    m_capacity = newCapacity;
+    assertBufferIsEmpty(this->buffer(), newCapacity);
 }

 template <typename HashMapBucketType>
-ALWAYS_INLINE void HashMapImpl<HashMapBucketType>::assertBufferIsEmpty() const
+ALWAYS_INLINE void HashMapImpl<HashMapBucketType>::assertBufferIsEmpty(HashMapBucketType** buffer, uint32_t capacity)
 {
-    if (ASSERT_ENABLED) {
-        for (unsigned i = 0; i < m_capacity; i++)
-            ASSERT(isEmpty(buffer()[i]));
-    }
+    UNUSED_PARAM(buffer);
+    UNUSED_PARAM(capacity);
+#if ASSERT_ENABLED
+    for (unsigned i = 0; i < capacity; i++)
+        ASSERT(isEmpty(buffer[i]));
+#endif
 }

 } // namespace JSC


@@ -45,12 +45,7 @@ std::pair<bool, std::reference_wrapper<JSC::JSObject>> getBackingSet(JSC::JSGlob
     auto backingSet = setLike.getDirect(vm, builtinNames(vm).backingSetPrivateName());
     if (!backingSet) {
         auto& vm = lexicalGlobalObject.vm();
-        JSC::DeferTermination deferScope(vm);
-        auto scope = DECLARE_CATCH_SCOPE(vm);
-
-        backingSet = JSC::JSSet::create(&lexicalGlobalObject, vm, lexicalGlobalObject.setStructure());
-        scope.releaseAssertNoException();
-
+        backingSet = JSC::JSSet::create(vm, lexicalGlobalObject.setStructure());
         setLike.putDirect(vm, builtinNames(vm).backingSetPrivateName(), backingSet, static_cast<unsigned>(JSC::PropertyAttribute::DontEnum));
         return { true, *JSC::asObject(backingSet) };
     }


shared/rm-rf: loop over nested directories instead of instead of recursing

To remove directory structures, we need to remove the innermost items first,
and then recursively remove higher-level directories. We would recursively
descend into directories and invoke rm_rf_children and rm_rm_children_inner.
This is problematic when too many directories are nested.

Instead, let's create a "TODO" queue. In the the queue, for each level we
hold the DIR* object we were working on, and the name of the directory. This
allows us to leave a partially-processed directory, and restart the removal
loop one level down. When done with the inner directory, we use the name to
unlinkat() it from the parent, and proceed with the removal of other items.

Because the nesting is increased by one level, it is best to view this patch
with -b/--ignore-space-change.

This fixes CVE-2021-3997, https://bugzilla.redhat.com/show_bug.cgi?id=2024639.
The issue was reported and patches reviewed by Qualys Team.
Mauro Matteo Cascella and Riccardo Schirone from Red Hat handled the disclosure.

@@ -52,7 +52,6 @@ static int patch_dirfd_mode(
 }

 int unlinkat_harder(int dfd, const char *filename, int unlink_flags, RemoveFlags remove_flags) {
-
         mode_t old_mode;
         int r;

@@ -116,12 +115,13 @@ int fstatat_harder(int dfd,
         return 0;
 }

-static int rm_rf_children_inner(
+static int rm_rf_inner_child(
                 int fd,
                 const char *fname,
                 int is_dir,
                 RemoveFlags flags,
-                const struct stat *root_dev) {
+                const struct stat *root_dev,
+                bool allow_recursion) {

         struct stat st;
         int r, q = 0;
@@ -141,9 +141,7 @@ static int rm_rf_children_inner(
         }

         if (is_dir) {
-                _cleanup_close_ int subdir_fd = -1;
-
-                /* if root_dev is set, remove subdirectories only if device is same */
+                /* If root_dev is set, remove subdirectories only if device is same */
                 if (root_dev && st.st_dev != root_dev->st_dev)
                         return 0;

@@ -155,7 +153,6 @@ static int rm_rf_children_inner(
                         return 0;

                 if ((flags & REMOVE_SUBVOLUME) && btrfs_might_be_subvol(&st)) {
-
                         /* This could be a subvolume, try to remove it */

                         r = btrfs_subvol_remove_fd(fd, fname, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
@@ -169,13 +166,16 @@ static int rm_rf_children_inner(
                                 return 1;
                 }

-                subdir_fd = openat(fd, fname, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+                if (!allow_recursion)
+                        return -EISDIR;
+
+                int subdir_fd = openat(fd, fname, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
                 if (subdir_fd < 0)
                         return -errno;

                 /* We pass REMOVE_PHYSICAL here, to avoid doing the fstatfs() to check the file system type
                  * again for each directory */
-                q = rm_rf_children(TAKE_FD(subdir_fd), flags | REMOVE_PHYSICAL, root_dev);
+                q = rm_rf_children(subdir_fd, flags | REMOVE_PHYSICAL, root_dev);

         } else if (flags & REMOVE_ONLY_DIRECTORIES)
                 return 0;
@@ -188,62 +188,127 @@ static int rm_rf_children_inner(
         return 1;
 }

+typedef struct TodoEntry {
+        DIR *dir;         /* A directory that we were operating on. */
+        char *dirname;    /* The filename of that directory itself. */
+} TodoEntry;
+
+static void free_todo_entries(TodoEntry **todos) {
+        for (TodoEntry *x = *todos; x && x->dir; x++) {
+                closedir(x->dir);
+                free(x->dirname);
+        }
+
+        freep(todos);
+}
+
 int rm_rf_children(
                 int fd,
                 RemoveFlags flags,
                 const struct stat *root_dev) {

-        _cleanup_closedir_ DIR *d = NULL;
+        _cleanup_(free_todo_entries) TodoEntry *todos = NULL;
+        size_t n_todo = 0;
+        _cleanup_free_ char *dirname = NULL; /* Set when we are recursing and want to delete ourselves */
         int ret = 0, r;

-        assert(fd >= 0);
+        /* Return the first error we run into, but nevertheless try to go on.
+         * The passed fd is closed in all cases, including on failure. */
+
+        for (;;) {  /* This loop corresponds to the directory nesting level. */
+                _cleanup_closedir_ DIR *d = NULL;
+
+                if (n_todo > 0) {
+                        /* We know that we are in recursion here, because n_todo is set.
+                         * We need to remove the inner directory we were operating on. */
+                        assert(dirname);
+                        r = unlinkat_harder(dirfd(todos[n_todo-1].dir), dirname, AT_REMOVEDIR, flags);
+                        if (r < 0 && r != -ENOENT && ret == 0)
+                                ret = r;
+                        dirname = mfree(dirname);
+
+                        /* And now let's back out one level up */
+                        n_todo --;
+                        d = TAKE_PTR(todos[n_todo].dir);
+                        dirname = TAKE_PTR(todos[n_todo].dirname);
+
+                        assert(d);
+                        fd = dirfd(d); /* Retrieve the file descriptor from the DIR object */
+                        assert(fd >= 0);
+                } else {
+        next_fd:
+                        assert(fd >= 0);
+                        d = fdopendir(fd);
+                        if (!d) {
+                                safe_close(fd);
+                                return -errno;
+                        }
+                        fd = dirfd(d); /* We donated the fd to fdopendir(). Let's make sure we sure we have
+                                        * the right descriptor even if it were to internally invalidate the
+                                        * one we passed. */
+
+                        if (!(flags & REMOVE_PHYSICAL)) {
+                                struct statfs sfs;
+
+                                if (fstatfs(fd, &sfs) < 0)
+                                        return -errno;
+
+                                if (is_physical_fs(&sfs)) {
+                                        /* We refuse to clean physical file systems with this call, unless
+                                         * explicitly requested. This is extra paranoia just to be sure we
+                                         * never ever remove non-state data. */
+
+                                        _cleanup_free_ char *path = NULL;
+
+                                        (void) fd_get_path(fd, &path);
+                                        return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+                                                               "Attempted to remove disk file system under \"%s\", and we can't allow that.",
+                                                               strna(path));
+                                }
+                        }
+                }

-        /* This returns the first error we run into, but nevertheless tries to go on. This closes the passed
-         * fd, in all cases, including on failure. */
+                FOREACH_DIRENT_ALL(de, d, return -errno) {
+                        int is_dir;

-        d = fdopendir(fd);
-        if (!d) {
-                safe_close(fd);
-                return -errno;
-        }
+                        if (dot_or_dot_dot(de->d_name))
+                                continue;

-        if (!(flags & REMOVE_PHYSICAL)) {
-                struct statfs sfs;
+                        is_dir = de->d_type == DT_UNKNOWN ? -1 : de->d_type == DT_DIR;

-                if (fstatfs(dirfd(d), &sfs) < 0)
-                        return -errno;
+                        r = rm_rf_inner_child(fd, de->d_name, is_dir, flags, root_dev, false);
+                        if (r == -EISDIR) {
+                                /* Push the current working state onto the todo list */

-                if (is_physical_fs(&sfs)) {
-                        /* We refuse to clean physical file systems with this call, unless explicitly
-                         * requested. This is extra paranoia just to be sure we never ever remove non-state
-                         * data. */
+                                 if (!GREEDY_REALLOC0(todos, n_todo + 2))
+                                         return log_oom();

-                        _cleanup_free_ char *path = NULL;
+                                 _cleanup_free_ char *newdirname = strdup(de->d_name);
+                                 if (!newdirname)
+                                         return log_oom();

-                        (void) fd_get_path(fd, &path);
-                        return log_error_errno(SYNTHETIC_ERRNO(EPERM),
-                                               "Attempted to remove disk file system under \"%s\", and we can't allow that.",
-                                               strna(path));
-                }
-        }
+                                 int newfd = openat(fd, de->d_name,
+                                                    O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+                                 if (newfd >= 0) {
+                                         todos[n_todo++] = (TodoEntry) { TAKE_PTR(d), TAKE_PTR(dirname) };
+                                         fd = newfd;
+                                         dirname = TAKE_PTR(newdirname);

-        FOREACH_DIRENT_ALL(de, d, return -errno) {
-                int is_dir;
+                                         goto next_fd;

-                if (dot_or_dot_dot(de->d_name))
-                        continue;
+                                 } else if (errno != -ENOENT && ret == 0)
+                                         ret = -errno;

-                is_dir =
-                        de->d_type == DT_UNKNOWN ? -1 :
-                        de->d_type == DT_DIR;
+                        } else if (r < 0 && r != -ENOENT && ret == 0)
+                                ret = r;
+                }

-                r = rm_rf_children_inner(dirfd(d), de->d_name, is_dir, flags, root_dev);
-                if (r < 0 && r != -ENOENT && ret == 0)
-                        ret = r;
-        }
+                if (FLAGS_SET(flags, REMOVE_SYNCFS) && syncfs(fd) < 0 && ret >= 0)
+                        ret = -errno;

-        if (FLAGS_SET(flags, REMOVE_SYNCFS) && syncfs(dirfd(d)) < 0 && ret >= 0)
-                ret = -errno;
+                if (n_todo == 0)
+                        break;
+        }

         return ret;
 }
@@ -335,5 +400,5 @@ int rm_rf_child(int fd, const char *name, RemoveFlags flags) {
         if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME))
                 return -EINVAL;

-        return rm_rf_children_inner(fd, name, -1, flags, NULL);
+        return rm_rf_inner_child(fd, name, -1, flags, NULL, true);
 }


Fix following symlinks when processing the fixup list

The previous fix in b41daecb5 was incomplete. Fixup entries are
given the original path without calling cleanup_pathname().
To make sure we don't follow a symlink, we must strip trailing
slashes from the path.

The fixup entries are always directories. Make sure we try to modify
only directories by providing O_DIRECTORY to open() (if supported)
and if it fails to check directory via lstat().

Fixes #1566

@@ -47,26 +47,50 @@ DEFINE_TEST(test_write_disk_fixup)
 	/*
 	 * Create a file
 	 */
-	assertMakeFile("victim", 0600, "a");
+	assertMakeFile("file", 0600, "a");
+
+	/*
+	 * Create a directory
+	 */
+	assertMakeDir("dir", 0700);

 	/*
 	 * Create a directory and a symlink with the same name
 	 */

-	/* Directory: dir */
+	/* Directory: dir1 */
+        assert((ae = archive_entry_new()) != NULL);
+        archive_entry_copy_pathname(ae, "dir1/");
+        archive_entry_set_mode(ae, AE_IFDIR | 0555);
+	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
+	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
+        archive_entry_free(ae);
+
+	/* Directory: dir2 */
         assert((ae = archive_entry_new()) != NULL);
-        archive_entry_copy_pathname(ae, "dir");
-        archive_entry_set_mode(ae, AE_IFDIR | 0606);
+        archive_entry_copy_pathname(ae, "dir2/");
+        archive_entry_set_mode(ae, AE_IFDIR | 0555);
 	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
 	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
         archive_entry_free(ae);

-	/* Symbolic Link: dir -> foo */
+	/* Symbolic Link: dir1 -> dir */
+	assert((ae = archive_entry_new()) != NULL);
+	archive_entry_copy_pathname(ae, "dir1");
+	archive_entry_set_mode(ae, AE_IFLNK | 0777);
+	archive_entry_set_size(ae, 0);
+	archive_entry_copy_symlink(ae, "dir");
+	assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
+	if (r >= ARCHIVE_WARN)
+		assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
+	archive_entry_free(ae);
+
+	/* Symbolic Link: dir2 -> file */
 	assert((ae = archive_entry_new()) != NULL);
-	archive_entry_copy_pathname(ae, "dir");
+	archive_entry_copy_pathname(ae, "dir2");
 	archive_entry_set_mode(ae, AE_IFLNK | 0777);
 	archive_entry_set_size(ae, 0);
-	archive_entry_copy_symlink(ae, "victim");
+	archive_entry_copy_symlink(ae, "file");
 	assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
 	if (r >= ARCHIVE_WARN)
 		assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
@@ -75,7 +99,9 @@ DEFINE_TEST(test_write_disk_fixup)
 	assertEqualInt(ARCHIVE_OK, archive_write_free(ad));

 	/* Test the entries on disk. */
-	assertIsSymlink("dir", "victim", 0);
-	assertFileMode("victim", 0600);
+	assertIsSymlink("dir1", "dir", 0);
+	assertIsSymlink("dir2", "file", 0);
+	assertFileMode("dir", 0700);
+	assertFileMode("file", 0600);
 #endif
 }


Fix handling of symbolic link ACLs

On Linux ACLs on symbolic links are not supported.
We must avoid calling acl_set_file() on symbolic links as their
targets are modified instead.

While here, do not try to set default ACLs on non-directories.

Fixes #1565

@@ -343,6 +343,11 @@ set_richacl(struct archive *a, int fd, const char *name,
 		return (ARCHIVE_FAILED);
 	}

+	if (S_ISLNK(mode)) {
+		/* Linux does not support RichACLs on symbolic links */
+		return (ARCHIVE_OK);
+	}
+
 	richacl = richacl_alloc(entries);
 	if (richacl == NULL) {
 		archive_set_error(a, errno,
@@ -455,7 +460,7 @@ set_richacl(struct archive *a, int fd, const char *name,
 #if ARCHIVE_ACL_LIBACL
 static int
 set_acl(struct archive *a, int fd, const char *name,
-    struct archive_acl *abstract_acl,
+    struct archive_acl *abstract_acl, __LA_MODE_T mode,
     int ae_requested_type, const char *tname)
 {
 	int		 acl_type = 0;
@@ -488,6 +493,18 @@ set_acl(struct archive *a, int fd, const char *name,
 		return (ARCHIVE_FAILED);
 	}

+	if (S_ISLNK(mode)) {
+		/* Linux does not support ACLs on symbolic links */
+		return (ARCHIVE_OK);
+	}
+
+	if (acl_type == ACL_TYPE_DEFAULT && !S_ISDIR(mode)) {
+		errno = EINVAL;
+		archive_set_error(a, errno,
+		    "Cannot set default ACL on non-directory");
+		return (ARCHIVE_WARN);
+	}
+
 	acl = acl_init(entries);
 	if (acl == (acl_t)NULL) {
 		archive_set_error(a, errno,
@@ -727,14 +744,14 @@ archive_write_disk_set_acls(struct archive *a, int fd, const char *name,
 	    & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) {
 		if ((archive_acl_types(abstract_acl)
 		    & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
-			ret = set_acl(a, fd, name, abstract_acl,
+			ret = set_acl(a, fd, name, abstract_acl, mode,
 			    ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access");
 			if (ret != ARCHIVE_OK)
 				return (ret);
 		}
 		if ((archive_acl_types(abstract_acl)
 		    & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0)
-			ret = set_acl(a, fd, name, abstract_acl,
+			ret = set_acl(a, fd, name, abstract_acl, mode,
 			    ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default");
 	}
 #endif	/* ARCHIVE_ACL_LIBACL */


Detect bogus DT_SYMENT.

https://github.com/upx/upx/issues/331
	modified:   p_lx_elf.cpp

@@ -1614,9 +1614,17 @@ PackLinuxElf32::invert_pt_dynamic(Elf32_Dyn const *dynp)
         unsigned const  z_sym = dt_table[Elf32_Dyn::DT_SYMENT];
         unsigned const sz_sym = !z_sym ? sizeof(Elf32_Sym)
             : get_te32(&dynp0[-1+ z_sym].d_val);
+        if (sz_sym < sizeof(Elf32_Sym)) {
+            char msg[50]; snprintf(msg, sizeof(msg),
+                "bad DT_SYMENT %x", sz_sym);
+            throwCantPack(msg);
+        }
         if (v_sym < v_str) {
             symnum_end = (v_str - v_sym) / sz_sym;
         }
+        if (symnum_end < 1) {
+            throwCantPack("bad DT_SYMTAB");
+        }
     }
     // DT_HASH often ends at DT_SYMTAB
     unsigned const v_hsh = elf_unsigned_dynamic(Elf32_Dyn::DT_HASH);
@@ -5104,9 +5112,17 @@ PackLinuxElf64::invert_pt_dynamic(Elf64_Dyn const *dynp)
         unsigned const  z_sym = dt_table[Elf64_Dyn::DT_SYMENT];
         unsigned const sz_sym = !z_sym ? sizeof(Elf64_Sym)
             : get_te64(&dynp0[-1+ z_sym].d_val);
+        if (sz_sym < sizeof(Elf64_Sym)) {
+            char msg[50]; snprintf(msg, sizeof(msg),
+                "bad DT_SYMENT %x", sz_sym);
+            throwCantPack(msg);
+        }
         if (v_sym < v_str) {
             symnum_end = (v_str - v_sym) / sz_sym;
         }
+        if (symnum_end < 1) {
+            throwCantPack("bad DT_SYMTAB");
+        }
     }
     // DT_HASH often ends at DT_SYMTAB
     unsigned const v_hsh = elf_unsigned_dynamic(Elf64_Dyn::DT_HASH);


Fixed security issue with passwords entered via a prompt

@@ -1,7 +1,7 @@
 /*
  * aescrypt.h
  *
- * Copyright (C) 2007-2017
+ * Copyright (C) 2007-2022
  *
  * This software is licensed as "freeware."  Permission to distribute
  * this software in source and binary forms is hereby granted without a
@@ -20,7 +20,8 @@
 #include "aes.h"
 #include "sha256.h"

-typedef struct {
+typedef struct
+{
     char aes[3];
     unsigned char version;
     unsigned char last_block_size;


avformat/mov: Check count sums in build_open_gop_key_points()

Fixes: ffmpeg.md
Fixes: Out of array access
Fixes: CVE-2022-2566

Found-by: Andy Nguyen <theflow@google.com>
Found-by: 3pvd <3pvd@google.com>
Reviewed-by: Andy Nguyen <theflow@google.com>
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>
(cherry picked from commit c953baa084607dd1d84c3bfcce3cf6a87c3e6e05)
Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>

@@ -3949,8 +3949,11 @@ static int build_open_gop_key_points(AVStream *st)

     /* Build an unrolled index of the samples */
     sc->sample_offsets_count = 0;
-    for (uint32_t i = 0; i < sc->ctts_count; i++)
+    for (uint32_t i = 0; i < sc->ctts_count; i++) {
+        if (sc->ctts_data[i].count > INT_MAX - sc->sample_offsets_count)
+            return AVERROR(ENOMEM);
         sc->sample_offsets_count += sc->ctts_data[i].count;
+    }
     av_freep(&sc->sample_offsets);
     sc->sample_offsets = av_calloc(sc->sample_offsets_count, sizeof(*sc->sample_offsets));
     if (!sc->sample_offsets)
@@ -3969,8 +3972,11 @@ static int build_open_gop_key_points(AVStream *st)
     /* Build a list of open-GOP key samples */
     sc->open_key_samples_count = 0;
     for (uint32_t i = 0; i < sc->sync_group_count; i++)
-        if (sc->sync_group[i].index == cra_index)
+        if (sc->sync_group[i].index == cra_index) {
+            if (sc->sync_group[i].count > INT_MAX - sc->open_key_samples_count)
+                return AVERROR(ENOMEM);
             sc->open_key_samples_count += sc->sync_group[i].count;
+        }
     av_freep(&sc->open_key_samples);
     sc->open_key_samples = av_calloc(sc->open_key_samples_count, sizeof(*sc->open_key_samples));
     if (!sc->open_key_samples)
@@ -3981,6 +3987,8 @@ static int build_open_gop_key_points(AVStream *st)
         if (sg->index == cra_index)
             for (uint32_t j = 0; j < sg->count; j++)
                 sc->open_key_samples[k++] = sample_id;
+        if (sg->count > INT_MAX - sample_id)
+            return AVERROR_PATCHWELCOME;
         sample_id += sg->count;
     }


squash heap-buffer-overflow, PoC TIFF from Hardik

@@ -1892,7 +1892,7 @@ static Image *ReadTIFFImage(const ImageInfo *image_info,
         /*
           Convert stripped TIFF image.
         */
-        extent=4*(samples_per_pixel+1)*TIFFStripSize(tiff);
+        extent=4*((image->depth+7)/8)*(samples_per_pixel+1)*TIFFStripSize(tiff);
         strip_pixels=(unsigned char *) AcquireQuantumMemory(extent,
           sizeof(*strip_pixels));
         if (strip_pixels == (unsigned char *) NULL)


Fix oob read on _luac_build_info and luac memleaks

@@ -193,6 +193,7 @@ void luac_add_entry(RzList *entry_list, ut64 offset, int entry_type);
 void luac_add_string(RzList *string_list, char *string, ut64 offset, ut64 size);

 LuacBinInfo *luac_build_info(LuaProto *proto);
+void luac_build_info_free(LuacBinInfo *bin_info);
 void _luac_build_info(LuaProto *proto, LuacBinInfo *info);

 /* ========================================================


Fix the heap buffer overflow in fribidi_cap_rtl_to_unicode

CapRTL charset is represented in ASCII range 1-127.
but an input to caprtl_to_unicode may be possibly more than that.

AddressSanitizer reports this like:
=================================================================
==1223446==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6150000002fc at pc 0x7fb94f3409fc bp 0x7ffca618
7190 sp 0x7ffca6187188
READ of size 4 at 0x6150000002fc thread T0
    #0 0x7fb94f3409fb in fribidi_cap_rtl_to_unicode ../lib/fribidi-char-sets-cap-rtl.c:235
    #1 0x402bda in main ../bin/fribidi-main.c:403
    #2 0x7fb94f15d58f in __libc_start_call_main (/lib64/libc.so.6+0x2d58f)
    #3 0x7fb94f15d648 in __libc_start_main_impl (/lib64/libc.so.6+0x2d648)
    #4 0x403714 in _start (/tmp/fribidi/build/bin/fribidi+0x403714)

0x6150000002fc is located 4 bytes to the left of 512-byte region [0x615000000300,0x615000000500)
allocated by thread T0 here:
    #0 0x7fb94f41d81f in __interceptor_malloc (/lib64/libasan.so.8+0xba81f)
    #1 0x7fb94f340025 in init_cap_rtl ../lib/fribidi-char-sets-cap-rtl.c:87
    #2 0x7fb94f3409e0 in fribidi_cap_rtl_to_unicode ../lib/fribidi-char-sets-cap-rtl.c:180
    #3 0x402bda in main ../bin/fribidi-main.c:403
    #4 0x7fb94f15d58f in __libc_start_call_main (/lib64/libc.so.6+0x2d58f)

So such input needs to be ignored.

This fixes https://github.com/fribidi/fribidi/issues/182

@@ -232,7 +232,12 @@ fribidi_cap_rtl_to_unicode (
 	    }
 	}
       else
-	us[j++] = caprtl_to_unicode[(int) s[i]];
+	{
+	  if (s[i] >= 0 && s[i] < CAPRTL_CHARS)
+	    us[j++] = caprtl_to_unicode[(int) s[i]];
+	  else
+	    us[j++] = s[i];
+	}
     }

   return j;


netfilter: nf_tables: disallow binding to already bound chain

Update nft_data_init() to report EINVAL if chain is already bound.

Fixes: d0e2c7de92c7 ("netfilter: nf_tables: add NFT_CHAIN_BINDING")
Reported-by: Gwangun Jung <exsociety@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

@@ -9711,6 +9711,8 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
 			return PTR_ERR(chain);
 		if (nft_is_base_chain(chain))
 			return -EOPNOTSUPP;
+		if (nft_chain_is_bound(chain))
+			return -EINVAL;
 		if (desc->flags & NFT_DATA_DESC_SETELEM &&
 		    chain->flags & NFT_CHAIN_BINDING)
 			return -EINVAL;


* Fixed Github Vulnerability Report GHSL-2022-066

@@ -26,7 +26,7 @@ static void _jbn_add_item(JBL_NODE parent, JBL_NODE node);

 void iwjson_ftoa(long double val, char buf[static IWNUMBUF_SIZE], size_t *out_len) {
   // TODO: review
-  int len = snprintf(buf, 64, "%.8Lf", val);
+  int len = snprintf(buf, IWNUMBUF_SIZE, "%.8Lf", val);
   if (len <= 0) {
     buf[0] = '\0';
     *out_len = 0;


Merge pull request from GHSA-wx5m-cj97-4wwg

@@ -1531,13 +1531,14 @@ static void srtp_rtp_cb(pjmedia_tp_cb_param *param)
 	pjmedia_srtp_crypto tx, rx;
 	pj_status_t status;

+	tx = srtp->tx_policy;
+	rx = srtp->rx_policy;
+
 	/* Stop SRTP first, otherwise srtp_start() will maintain current
 	 * roll-over counter.
 	 */
 	pjmedia_transport_srtp_stop((pjmedia_transport*)srtp);

-	tx = srtp->tx_policy;
-	rx = srtp->rx_policy;
 	status = pjmedia_transport_srtp_start((pjmedia_transport*)srtp,
 					      &tx, &rx);
 	if (status != PJ_SUCCESS) {


Fixed invalid memory access bugs when decoding JPEG Exif data

Fixes issues #22, #23, #24, #25

@@ -58,6 +58,18 @@ struct iw_exif_state {
 	size_t d_len;
 };

+static unsigned int get_exif_ui16(struct iw_exif_state *e, unsigned int pos)
+{
+	if(e->d_len<2 || pos>e->d_len-2) return 0;
+	return iw_get_ui16_e(&e->d[pos], e->endian);
+}
+
+static unsigned int get_exif_ui32(struct iw_exif_state *e, unsigned int pos)
+{
+	if(e->d_len<4 || pos>e->d_len-4) return 0;
+	return iw_get_ui32_e(&e->d[pos], e->endian);
+}
+
 // Try to read an Exif tag into an integer.
 // Returns zero on failure.
 static int get_exif_tag_int_value(struct iw_exif_state *e, unsigned int tag_pos,
@@ -66,17 +78,17 @@ static int get_exif_tag_int_value(struct iw_exif_state *e, unsigned int tag_pos,
 	unsigned int field_type;
 	unsigned int value_count;

-	field_type = iw_get_ui16_e(&e->d[tag_pos+2],e->endian);
-	value_count = iw_get_ui32_e(&e->d[tag_pos+4],e->endian);
+	field_type = get_exif_ui16(e, tag_pos+2);
+	value_count = get_exif_ui32(e, tag_pos+4);

 	if(value_count!=1) return 0;

 	if(field_type==3) { // SHORT (uint16)
-		*pv = iw_get_ui16_e(&e->d[tag_pos+8],e->endian);
+		*pv = get_exif_ui16(e, tag_pos+8);
 		return 1;
 	}
 	else if(field_type==4) { // LONG (uint32)
-		*pv = iw_get_ui32_e(&e->d[tag_pos+8],e->endian);
+		*pv = get_exif_ui32(e, tag_pos+8);
 		return 1;
 	}

@@ -93,8 +105,8 @@ static int get_exif_tag_dbl_value(struct iw_exif_state *e, unsigned int tag_pos,
 	unsigned int value_pos;
 	unsigned int numer, denom;

-	field_type = iw_get_ui16_e(&e->d[tag_pos+2],e->endian);
-	value_count = iw_get_ui32_e(&e->d[tag_pos+4],e->endian);
+	field_type = get_exif_ui16(e, tag_pos+2);
+	value_count = get_exif_ui32(e, tag_pos+4);

 	if(value_count!=1) return 0;

@@ -103,12 +115,12 @@ static int get_exif_tag_dbl_value(struct iw_exif_state *e, unsigned int tag_pos,
 	// A rational is 8 bytes. Since 8>4, it is stored indirectly. First, read
 	// the location where it is stored.

-	value_pos = iw_get_ui32_e(&e->d[tag_pos+8],e->endian);
+	value_pos = get_exif_ui32(e, tag_pos+8);
 	if(value_pos > e->d_len-8) return 0;

 	// Read the actual value.
-	numer = iw_get_ui32_e(&e->d[value_pos  ],e->endian);
-	denom = iw_get_ui32_e(&e->d[value_pos+4],e->endian);
+	numer = get_exif_ui32(e, value_pos);
+	denom = get_exif_ui32(e, value_pos+4);
 	if(denom==0) return 0;

 	*pv = ((double)numer)/denom;
@@ -125,15 +137,15 @@ static void iwjpeg_scan_exif_ifd(struct iwjpegrcontext *rctx,
 	unsigned int v;
 	double v_dbl;

-	if(ifd<8 || ifd>e->d_len-18) return;
+	if(ifd<8 || e->d_len<18 || ifd>e->d_len-18) return;

-	tag_count = iw_get_ui16_e(&e->d[ifd],e->endian);
+	tag_count = get_exif_ui16(e, ifd);
 	if(tag_count>1000) return; // Sanity check.

 	for(i=0;i<tag_count;i++) {
 		tag_pos = ifd+2+i*12;
 		if(tag_pos+12 > e->d_len) return; // Avoid overruns.
-		tag_id = iw_get_ui16_e(&e->d[tag_pos],e->endian);
+		tag_id = get_exif_ui16(e, tag_pos);

 		switch(tag_id) {
 		case 274: // 274 = Orientation
@@ -177,7 +189,7 @@ static void iwjpeg_scan_exif(struct iwjpegrcontext *rctx,

 	e.endian = d[0]=='I' ? IW_ENDIAN_LITTLE : IW_ENDIAN_BIG;

-	ifd = iw_get_ui32_e(&d[4],e.endian);
+	ifd = get_exif_ui32(&e, 4);

 	iwjpeg_scan_exif_ifd(rctx,&e,ifd);
 }


@@ -86,6 +86,11 @@ void yr_object_destroy(
     YR_OBJECT* object);

+int yr_object_copy(
+    YR_OBJECT* object,
+    YR_OBJECT** object_copy);
+
+
 YR_OBJECT* yr_object_lookup_field(
     YR_OBJECT* object,
     const char* field_name);


fs: Don't try to delete the file when copying. It could cause a security issue if the file exists and doesn't allow other's to read/write. delete could allow someone to create the file and have access to the data.

@@ -101,6 +101,15 @@ static M_bool M_fs_isfileintodir(const char *p1, const char *p2, char **new_p2)
 	return M_TRUE;
 }

+/* Used by copy and move to determine if we can write to the given path
+ * based on a file already existing there or not.
+ *
+ * access is used to determine existence because we don't want to overwrite
+ * if there already is a file. This is not guaranteed because if there is
+ * a race condition where a file is created after this check it will be
+ * overwritten. Not much we can do about that. It shouldn't pose a security
+ * issue since this is more of a request than a requirement.
+ */
 static M_bool M_fs_check_overwrite_allowed(const char *p1, const char *p2, M_uint32 mode)
 {
 	M_fs_info_t  *info = NULL;
@@ -129,8 +138,7 @@ static M_bool M_fs_check_overwrite_allowed(const char *p1, const char *p2, M_uin

 	if (type != M_FS_TYPE_DIR) {
 		/* File exists at path. */
-		if (M_fs_perms_can_access(p2, M_FS_PERMS_MODE_NONE) == M_FS_ERROR_SUCCESS)
-		{
+		if (M_fs_perms_can_access(p2, M_FS_PERMS_MODE_NONE) == M_FS_ERROR_SUCCESS) {
 			ret = M_FALSE;
 			goto done;
 		}
@@ -209,19 +217,6 @@ static M_fs_error_t M_fs_copy_file(const char *path_old, const char *path_new, M
 	size_t         offset;
 	M_fs_error_t   res;

-	/* We're going to create/open/truncate the new file, then as we read the contents from the old file we'll write it
- 	 * to new file. */
-	if (M_fs_perms_can_access(path_new, M_FS_PERMS_MODE_NONE) == M_FS_ERROR_SUCCESS) {
-		/* Try to delete the file since we'll be overwrite it. This is so when we create the file we create it without
- 		 * any permissions and to ensure that anything that has the file already open won't be able to read the new
-		 * contents we're writing to the file or be able to change the perms. There is an unavoidable race condition
-		 * between deleting and creating the file where someone could create the file and have access. However,
-		 * depending on the OS they may have access even if the file is created with no perms... */
-		res = M_fs_delete(path_new, M_FALSE, NULL, M_FS_PROGRESS_NOEXTRA);
-		if (res != M_FS_ERROR_SUCCESS) {
-			return res;
-		}
-	}
 	/* Open the old file */
 	res = M_fs_file_open(&fd_old, path_old, M_FS_BUF_SIZE, M_FS_FILE_MODE_READ|M_FS_FILE_MODE_NOCREATE, NULL);
 	if (res != M_FS_ERROR_SUCCESS) {
@@ -236,6 +231,9 @@ static M_fs_error_t M_fs_copy_file(const char *path_old, const char *path_new, M
 		}
 		perms = M_fs_info_get_perms(info);
 	}
+
+	/* We're going to create/open/truncate the new file, then as we read the contents from the old file we'll write it
+	 * to new file. */
 	res = M_fs_file_open(&fd_new, path_new, M_FS_BUF_SIZE, M_FS_FILE_MODE_WRITE|M_FS_FILE_MODE_OVERWRITE, perms);
 	M_fs_info_destroy(info);
 	if (res != M_FS_ERROR_SUCCESS) {
@@ -333,7 +331,7 @@ M_fs_error_t M_fs_move(const char *path_old, const char *path_new, M_uint32 mode
 	}

 	/* Normalize the old path and do basic checks that it exists. We'll leave really checking that the old path
- 	 * existing to rename because any check we perform may not be true when rename is called. */
+	 * existing to rename because any check we perform may not be true when rename is called. */
 	res = M_fs_path_norm(&norm_path_old, path_old, M_FS_PATH_NORM_RESALL, M_FS_SYSTEM_AUTO);
 	if (res != M_FS_ERROR_SUCCESS) {
 		M_free(norm_path_new);
@@ -351,7 +349,7 @@ M_fs_error_t M_fs_move(const char *path_old, const char *path_new, M_uint32 mode
 		return res;
 	}

- 	/* There is a race condition where the path could not exist but be created between the exists check and calling
+	/* There is a race condition where the path could not exist but be created between the exists check and calling
 	 * rename to move the file but there isn't much we can do in this case. copy will delete and the file so this
 	 * situation won't cause an error. */
 	if (!M_fs_check_overwrite_allowed(norm_path_old, norm_path_new, mode)) {
@@ -399,15 +397,15 @@ M_fs_error_t M_fs_move(const char *path_old, const char *path_new, M_uint32 mode
 			res = M_fs_delete(norm_path_old, M_TRUE, NULL, M_FS_PROGRESS_NOEXTRA);
 		} else {
 			/* Failure - Delete the new files that were copied but only if we are not overwriting. We don't
- 			 * want to remove any existing files (especially if the dest is a dir). */
+			 * want to remove any existing files (especially if the dest is a dir). */
 			if (!(mode & M_FS_FILE_MODE_OVERWRITE)) {
 				M_fs_delete(norm_path_new, M_TRUE, NULL, M_FS_PROGRESS_NOEXTRA);
 			}
 			res = M_FS_ERROR_GENERIC;
 		}
 	} else {
 		/* Call the cb with the result of the move whether it was a success for fail. We call the cb only if the
- 		 * result of the move is not M_FS_ERROR_NOT_SAMEDEV because the copy operation will call the cb for us. */
+		 * result of the move is not M_FS_ERROR_NOT_SAMEDEV because the copy operation will call the cb for us. */
 		if (cb) {
 			M_fs_progress_set_result(progress, res);
 			if (!cb(progress)) {
@@ -465,7 +463,7 @@ M_fs_error_t M_fs_copy(const char *path_old, const char *path_new, M_uint32 mode
 	}

 	/* Normalize the old path and do basic checks that it exists. We'll leave really checking that the old path
- 	 * existing to rename because any check we perform may not be true when rename is called. */
+	 * existing to rename because any check we perform may not be true when rename is called. */
 	res = M_fs_path_norm(&norm_path_old, path_old, M_FS_PATH_NORM_RESALL, M_FS_SYSTEM_AUTO);
 	if (res != M_FS_ERROR_SUCCESS) {
 		M_free(norm_path_new);
@@ -485,7 +483,7 @@ M_fs_error_t M_fs_copy(const char *path_old, const char *path_new, M_uint32 mode

 	type = M_fs_info_get_type(info);

- 	/* There is a race condition where the path could not exist but be created between the exists check and calling
+	/* There is a race condition where the path could not exist but be created between the exists check and calling
 	 * rename to move the file but there isn't much we can do in this case. copy will delete and the file so this
 	 * situation won't cause an error. */
 	if (!M_fs_check_overwrite_allowed(norm_path_old, norm_path_new, mode)) {
@@ -497,7 +495,7 @@ M_fs_error_t M_fs_copy(const char *path_old, const char *path_new, M_uint32 mode

 	entries = M_fs_dir_entries_create();
 	/* No need to destroy info  because it's now owned by entries and will be destroyed when entries is destroyed.
- 	 * M_FS_DIR_WALK_FILTER_READ_INFO_BASIC doesn't actually get the perms it's just there to ensure the info is
+	 * M_FS_DIR_WALK_FILTER_READ_INFO_BASIC doesn't actually get the perms it's just there to ensure the info is
 	 * stored in the entry. */
 	M_fs_dir_entries_insert(entries, M_fs_dir_walk_fill_entry(norm_path_new, NULL, type, info, M_FS_DIR_WALK_FILTER_READ_INFO_BASIC));
 	if (type == M_FS_TYPE_DIR) {
@@ -523,7 +521,7 @@ M_fs_error_t M_fs_copy(const char *path_old, const char *path_new, M_uint32 mode

 			type = M_fs_dir_entry_get_type(entry);
 			/* The total isn't the total number of files but the total number of operations.
- 			 * Making dirs and symlinks is one operation and copying a file will be split into
+			 * Making dirs and symlinks is one operation and copying a file will be split into
 			 * multiple operations. Copying uses the M_FS_BUF_SIZE to read and write in
 			 * chunks. We determine how many chunks will be needed to read the entire file and
 			 * use that for the number of operations for the file. */
@@ -600,7 +598,7 @@ M_fs_error_t M_fs_copy(const char *path_old, const char *path_new, M_uint32 mode
 	}

 	/* Delete the file(s) if it could not be copied properly, but only if we are not overwriting.
- 	 * If we're overwriting then there could be other files in that location (especially if it's a dir). */
+	 * If we're overwriting then there could be other files in that location (especially if it's a dir). */
 	if (res != M_FS_ERROR_SUCCESS && !(mode & M_FS_FILE_MODE_OVERWRITE)) {
 		M_fs_delete(path_new, M_TRUE, NULL, M_FS_PROGRESS_NOEXTRA);
 	}
@@ -659,7 +657,7 @@ M_fs_error_t M_fs_delete(const char *path, M_bool remove_children, M_fs_progress
 	entries = M_fs_dir_entries_create();

 	/* Recursive directory deletion isn't intuitive. We have to generate a list of files and delete the list.
- 	 * We cannot delete as walk because not all file systems support that operation. The walk; delete; behavior
+	 * We cannot delete as walk because not all file systems support that operation. The walk; delete; behavior
 	 * is undefined in Posix and HFS is known to skip files if the directory contents is modifies as the
 	 * directory is being walked. */
 	if (type == M_FS_TYPE_DIR && remove_children) {
@@ -671,7 +669,7 @@ M_fs_error_t M_fs_delete(const char *path, M_bool remove_children, M_fs_progress
 	}

 	/* Add the original path to the list of entries. This may be the only entry in the list. We need to add
- 	 * it after a potential walk because we can't delete a directory that isn't empty.
+	 * it after a potential walk because we can't delete a directory that isn't empty.
 	 * Note:
 	 *   - The info will be owned by the entry and destroyed when it is destroyed.
 	 *   - The basic info param doesn't get the info in this case. it's set so the info is stored in the entry. */
@@ -680,7 +678,7 @@ M_fs_error_t M_fs_delete(const char *path, M_bool remove_children, M_fs_progress
 	len = M_fs_dir_entries_len(entries);
 	if (cb) {
 		/* Create the progress. The same progress will be used for the entire operation. It will be updated with
- 		 * new info as necessary. */
+		 * new info as necessary. */
 		progress = M_fs_progress_create();

 		/* Get the total size of all files to be deleted if using the progress cb and size totals is set. */


Merge pull request #131 from benjaminchodroff/master

fix memory corruption and other 32bit overflows

@@ -32,6 +32,7 @@ typedef struct rpc_pkg {
 # pragma pack()

 # define RPC_PKG_HEAD_SIZE (sizeof(rpc_pkg) - sizeof(void *) * 2)
+# define RPC_PKG_MAX_BODY_SIZE ((UINT32_MAX / 2) - UINT16_MAX - sizeof(rpc_pkg))

 int rpc_decode(nw_ses *ses, void *data, size_t max);
 int rpc_pack(rpc_pkg *pkg, void **data, uint32_t *size);


Extend stack when pushing arguments that does not fit in; fix #4038

@@ -184,26 +184,27 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
 {
   struct mrb_context *c = fiber_check(mrb, self);
   struct mrb_context *old_c = mrb->c;
+  enum mrb_fiber_state status;
   mrb_value value;

   fiber_check_cfunc(mrb, c);
-  if (resume && c->status == MRB_FIBER_TRANSFERRED) {
+  status = c->status;
+  if (resume && status == MRB_FIBER_TRANSFERRED) {
     mrb_raise(mrb, E_FIBER_ERROR, "resuming transferred fiber");
   }
-  if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMED) {
+  if (status == MRB_FIBER_RUNNING || status == MRB_FIBER_RESUMED) {
     mrb_raise(mrb, E_FIBER_ERROR, "double resume (fib)");
   }
-  if (c->status == MRB_FIBER_TERMINATED) {
+  if (status == MRB_FIBER_TERMINATED) {
     mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber");
   }
-  mrb->c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;
+  old_c->status = resume ? MRB_FIBER_RESUMED : MRB_FIBER_TRANSFERRED;
   c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c);
-  if (c->status == MRB_FIBER_CREATED) {
+  fiber_switch_context(mrb, c);
+  if (status == MRB_FIBER_CREATED) {
     mrb_value *b, *e;

-    if (len >= c->stend - c->stack) {
-      mrb_raise(mrb, E_FIBER_ERROR, "too many arguments to fiber");
-    }
+    mrb_stack_extend(mrb, len+2); /* for receiver and (optional) block */
     b = c->stack+1;
     e = b + len;
     while (b<e) {
@@ -215,7 +216,6 @@ fiber_switch(mrb_state *mrb, mrb_value self, mrb_int len, const mrb_value *a, mr
   else {
     value = fiber_result(mrb, a, len);
   }
-  fiber_switch_context(mrb, c);

   if (vmexec) {
     c->vmexec = TRUE;


Fix strncat/cpy bounding issues (fix #1425)

@@ -814,19 +814,14 @@ void jslTokenAsString(int token, char *str, size_t len) {
   }

   assert(len>=10);
-  strncpy(str, "?[",len);
-  itostr(token, &str[2], 10);
-  strncat(str, "]",len);
+  espruino_snprintf(str, len, "?[%d]", token);
 }

 void jslGetTokenString(char *str, size_t len) {
   if (lex->tk == LEX_ID) {
-    strncpy(str, "ID:", len);
-    strncat(str, jslGetTokenValueAsString(), len);
+    espruino_snprintf(str, len, "ID:%s", jslGetTokenValueAsString());
   } else if (lex->tk == LEX_STR) {
-    strncpy(str, "String:'", len);
-    strncat(str, jslGetTokenValueAsString(), len);
-    strncat(str, "'", len);
+    espruino_snprintf(str, len, "String:'%s'", jslGetTokenValueAsString());
   } else
     jslTokenAsString(lex->tk, str, len);
 }


igmp: Avoid zero delay when receiving odd mixture of IGMP queries

commit a8c1f65c79cbbb2f7da782d4c9d15639a9b94b27 upstream.

Commit 5b7c84066733c5dfb0e4016d939757b38de189e4 ('ipv4: correct IGMP
behavior on v3 query during v2-compatibility mode') added yet another
case for query parsing, which can result in max_delay = 0.  Substitute
a value of 1, as in the usual v3 case.

Reported-by: Simon McVittie <smcv@debian.org>
References: http://bugs.debian.org/654876
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>

@@ -875,6 +875,8 @@ static void igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
 		 * to be intended in a v3 query.
 		 */
 		max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE);
+		if (!max_delay)
+			max_delay = 1;	/* can't mod w/ 0 */
 	} else { /* v3 */
 		if (!pskb_may_pull(skb, sizeof(struct igmpv3_query)))
 			return;


GFS2: rewrite fallocate code to write blocks directly

GFS2's fallocate code currently goes through the page cache. Since it's only
writing to the end of the file or to holes in it, it doesn't need to, and it
was causing issues on low memory environments. This patch pulls in some of
Steve's block allocation work, and uses it to simply allocate the blocks for
the file, and zero them out at allocation time.  It provides a slight
performance increase, and it dramatically simplifies the code.

Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>

@@ -103,12 +103,15 @@ struct gfs2_rgrpd {
 enum gfs2_state_bits {
 	BH_Pinned = BH_PrivateStart,
 	BH_Escaped = BH_PrivateStart + 1,
+	BH_Zeronew = BH_PrivateStart + 2,
 };

 BUFFER_FNS(Pinned, pinned)
 TAS_BUFFER_FNS(Pinned, pinned)
 BUFFER_FNS(Escaped, escaped)
 TAS_BUFFER_FNS(Escaped, escaped)
+BUFFER_FNS(Zeronew, zeronew)
+TAS_BUFFER_FNS(Zeronew, zeronew)

 struct gfs2_bufdata {
 	struct buffer_head *bd_bh;


@@ -300,11 +300,21 @@ static void lsr_read_extension(GF_LASeRCodec *lsr, const char *name)

 static void lsr_read_extend_class(GF_LASeRCodec *lsr, char **out_data, u32 *out_len, const char *name)
 {
-	u32 len;
+	u32 len, blen;
 	GF_LSR_READ_INT(lsr, len, lsr->info->cfg.extensionIDBits, "reserved");
 	len = lsr_read_vluimsbf5(lsr, "len");
-//	while (len) gf_bs_read_int(lsr->bs, 1);
-	gf_bs_read_long_int(lsr->bs, len);
+	while (len && !gf_bs_is_align(lsr->bs)) {
+		gf_bs_read_int(lsr->bs, len);
+		len--;
+	}
+	blen = len / 8;
+	gf_bs_skip_bytes(lsr->bs, blen);
+	len -= blen*8;
+
+	while (len) {
+		gf_bs_read_int(lsr->bs, 1);
+		len--;
+	}
 	if (out_data) *out_data = NULL;
 	if (out_len) *out_len = 0;
 }
@@ -839,10 +849,11 @@ static void lsr_read_id(GF_LASeRCodec *lsr, GF_Node *n)
 static Fixed lsr_translate_coords(GF_LASeRCodec *lsr, u32 val, u32 nb_bits)
 {
 	if (!nb_bits) return 0;
-
+	if (nb_bits>=32) return 0;
+
 #ifdef GPAC_FIXED_POINT
 	if (val >> (nb_bits-1) ) {
-		s32 neg = (s32) val - (1<<nb_bits);
+		s64 neg = (s64) val - (0x00000001UL << nb_bits);
 		if (neg < -FIX_ONE / 2)
 			return 2 * gf_divfix(INT2FIX(neg/2), lsr->res_factor);
 		return gf_divfix(INT2FIX(neg), lsr->res_factor);
@@ -853,18 +864,18 @@ static Fixed lsr_translate_coords(GF_LASeRCodec *lsr, u32 val, u32 nb_bits)
 	}
 #else
 	if (val >> (nb_bits-1) ) {
-		s32 neg = (s32) val - (1<<nb_bits);
-		return gf_divfix(INT2FIX(neg), lsr->res_factor);
+		s64 neg = (s64) val - (0x00000001UL << nb_bits);
+		return ((Fixed)neg) / lsr->res_factor;
 	} else {
-		return gf_divfix(INT2FIX(val), lsr->res_factor);
+		return ((Fixed)val) / lsr->res_factor;
 	}
 #endif
 }

 static Fixed lsr_translate_scale(GF_LASeRCodec *lsr, u32 val)
 {
 	if (val >> (lsr->coord_bits-1) ) {
-		s32 v = val - (1<<lsr->coord_bits);
+		s64 v = val - (0x00000001UL << lsr->coord_bits);
 		return INT2FIX(v) / 256 ;
 	} else {
 		return INT2FIX(val) / 256;


Fixed missing input buffer length check in urbdrc

(cherry picked from commit 497df00f741dd4fc89292aaef2db7368aee45d0d)

@@ -247,6 +247,10 @@ static UINT urbdrc_process_io_control(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* c

 	Stream_Read_UINT32(s, OutputBufferSize);
 	Stream_Read_UINT32(s, RequestId);
+
+	if (OutputBufferSize > UINT32_MAX - 4)
+		return ERROR_INVALID_DATA;
+
 	InterfaceId = ((STREAM_ID_PROXY << 30) | pdev->get_ReqCompletion(pdev));
 	out = urb_create_iocompletion(InterfaceId, MessageId, RequestId, OutputBufferSize + 4);

@@ -726,6 +730,15 @@ static UINT urb_bulk_or_interrupt_transfer(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBA
 	Stream_Read_UINT32(s, TransferFlags); /** TransferFlags */
 	Stream_Read_UINT32(s, OutputBufferSize);
 	EndpointAddress = (PipeHandle & 0x000000ff);
+
+	if (transferDir == USBD_TRANSFER_DIRECTION_OUT)
+	{
+		if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize))
+		{
+			return ERROR_INVALID_DATA;
+		}
+	}
+
 	/**  process TS_URB_BULK_OR_INTERRUPT_TRANSFER */
 	return pdev->bulk_or_interrupt_transfer(
 	    pdev, callback, MessageId, RequestId, EndpointAddress, TransferFlags, noAck,
@@ -810,6 +823,13 @@ static UINT urb_isoch_transfer(IUDEVICE* pdev, URBDRC_CHANNEL_CALLBACK* callback
 	packetDescriptorData = Stream_Pointer(s);
 	Stream_Seek(s, NumberOfPackets * 12);
 	Stream_Read_UINT32(s, OutputBufferSize);
+
+	if (transferDir == USBD_TRANSFER_DIRECTION_OUT)
+	{
+		if (!Stream_CheckAndLogRequiredLength(TAG, s, OutputBufferSize))
+			return ERROR_INVALID_DATA;
+	}
+
 	return pdev->isoch_transfer(
 	    pdev, callback, MessageId, RequestId, EndpointAddress, TransferFlags, StartFrame,
 	    ErrorCount, noAck, packetDescriptorData, NumberOfPackets, OutputBufferSize,


time-util: fix buffer-over-run

Fixes #23928.

@@ -591,7 +591,7 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
                         t = b;
                 }

-                n = MIN((size_t) k, l);
+                n = MIN((size_t) k, l-1);

                 l -= n;
                 p += n;