diff --git a/_gdb.spec.Patch.include b/_gdb.spec.Patch.include
index ba7cbe9..ce58612 100644
--- a/_gdb.spec.Patch.include
+++ b/_gdb.spec.Patch.include
@@ -407,3 +407,9 @@ Patch099: gdb-rhbz1930528-fix-gnulib-build-error.patch
 # (RH BZ 1932645).
 Patch100: gdb-rhbz1932645-aarch64-ptrace-header-order.patch
 
+# Backport fix for frame_id_p assertion failure (RH BZ 1909902).
+Patch101: gdb-rhbz1909902-frame_id_p-assert-1.patch
+
+# Backport patch #2 which fixes a frame_id_p assertion failure (RH BZ 1909902).
+Patch102: gdb-rhbz1909902-frame_id_p-assert-2.patch
+
diff --git a/_gdb.spec.patch.include b/_gdb.spec.patch.include
index fd3cf02..48f09cd 100644
--- a/_gdb.spec.patch.include
+++ b/_gdb.spec.patch.include
@@ -98,3 +98,5 @@
 %patch098 -p1
 %patch099 -p1
 %patch100 -p1
+%patch101 -p1
+%patch102 -p1
diff --git a/_patch_order b/_patch_order
index 76a6a2f..9229088 100644
--- a/_patch_order
+++ b/_patch_order
@@ -98,3 +98,5 @@ gdb-rhbz1905996-fix-off-by-one-error-in-ada_fold_name.patch
 gdb-rhbz1912985-libstdc++-assert.patch
 gdb-rhbz1930528-fix-gnulib-build-error.patch
 gdb-rhbz1932645-aarch64-ptrace-header-order.patch
+gdb-rhbz1909902-frame_id_p-assert-1.patch
+gdb-rhbz1909902-frame_id_p-assert-2.patch
diff --git a/gdb-rhbz1909902-frame_id_p-assert-1.patch b/gdb-rhbz1909902-frame_id_p-assert-1.patch
new file mode 100644
index 0000000..7bced66
--- /dev/null
+++ b/gdb-rhbz1909902-frame_id_p-assert-1.patch
@@ -0,0 +1,593 @@
+From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
+From: Pedro Alves <pedro@palves.net>
+Date: Fri, 30 Oct 2020 18:26:15 +0100
+Subject: gdb-rhbz1909902-frame_id_p-assert-1.patch
+
+;; Backport fix for frame_id_p assertion failure (RH BZ 1909902).
+
+Make scoped_restore_current_thread's cdtors exception free (RFC)
+
+If the remote target closes while we're reading registers/memory for
+restoring the selected frame in scoped_restore_current_thread's dtor,
+the corresponding TARGET_CLOSE_ERROR error is swallowed by the
+scoped_restore_current_thread's dtor, because letting exceptions
+escape from a dtor is bad.  It isn't great to lose that errors like
+that, though.  I've been thinking about how to avoid it, and I came up
+with this patch.
+
+The idea here is to make scoped_restore_current_thread's dtor do as
+little as possible, to avoid any work that might throw in the first
+place.  And to do that, instead of having the dtor call
+restore_selected_frame, which re-finds the previously selected frame,
+just record the frame_id/level of the desired selected frame, and have
+get_selected_frame find the frame the next time it is called.  In
+effect, this implements most of Cagney's suggestion, here:
+
+  /* On demand, create the selected frame and then return it.  If the
+     selected frame can not be created, this function prints then throws
+     an error.  When MESSAGE is non-NULL, use it for the error message,
+     otherwize use a generic error message.  */
+  /* FIXME: cagney/2002-11-28: At present, when there is no selected
+     frame, this function always returns the current (inner most) frame.
+     It should instead, when a thread has previously had its frame
+     selected (but not resumed) and the frame cache invalidated, find
+     and then return that thread's previously selected frame.  */
+  extern struct frame_info *get_selected_frame (const char *message);
+
+The only thing missing to fully implement that would be to make
+reinit_frame_cache just clear selected_frame instead of calling
+select_frame(NULL), and the call select_frame(NULL) explicitly in the
+places where we really wanted reinit_frame_cache to go back to the
+current frame too.  That can done separately, though, I'm not
+proposing to do that in this patch.
+
+Note that this patch renames restore_selected_frame to
+lookup_selected_frame, and adds a new restore_selected_frame function
+that doesn't throw, to be paired with the also-new save_selected_frame
+function.
+
+There's a restore_selected_frame function in infrun.c that I think can
+be replaced by the new one in frame.c.
+
+Also done in this patch is make the get_selected_frame's parameter be
+optional, so that we don't have to pass down nullptr explicitly all
+over the place.
+
+lookup_selected_frame should really move from thread.c to frame.c, but
+I didn't do that here, just to avoid churn in the patch while it
+collects comments.  I did make it extern and declared it in frame.h
+already, preparing for the move.  I will do the move as a follow up
+patch if people agree with this approach.
+
+Incidentally, this patch alone would fix the crashes fixed by the
+previous patches in the series, because with this,
+scoped_restore_current_thread's constructor doesn't throw either.
+
+gdb/ChangeLog:
+
+	* blockframe.c (block_innermost_frame): Use get_selected_frame.
+	* frame.c
+	(scoped_restore_selected_frame::scoped_restore_selected_frame):
+	Use save_selected_frame.  Save language as well.
+	(scoped_restore_selected_frame::~scoped_restore_selected_frame):
+	Use restore_selected_frame, and restore language as well.
+	(selected_frame_id, selected_frame_level): New.
+	(selected_frame): Update comments.
+	(save_selected_frame, restore_selected_frame): New.
+	(get_selected_frame): Use lookup_selected_frame.
+	(get_selected_frame_if_set): Delete.
+	(select_frame): Record selected_frame_level and selected_frame_id.
+	* frame.h (scoped_restore_selected_frame) <m_level, m_lang>: New
+	fields.
+	(get_selected_frame): Make 'message' parameter optional.
+	(get_selected_frame_if_set): Delete declaration.
+	(select_frame): Update comments.
+	(save_selected_frame, restore_selected_frame)
+	(lookup_selected_frame): Declare.
+	* gdbthread.h (scoped_restore_current_thread) <m_lang>: New field.
+	* infrun.c (struct infcall_control_state) <selected_frame_level>:
+	New field.
+	(save_infcall_control_state): Use save_selected_frame.
+	(restore_selected_frame): Delete.
+	(restore_infcall_control_state): Use restore_selected_frame.
+	* stack.c (select_frame_command_core, frame_command_core): Use
+	get_selected_frame.
+	* thread.c (restore_selected_frame): Rename to ...
+	(lookup_selected_frame): ... this and make extern.  Select the
+	current frame if the frame level is -1.
+	(scoped_restore_current_thread::restore): Also restore the
+	language.
+	(scoped_restore_current_thread::~scoped_restore_current_thread):
+	Don't try/catch.
+	(scoped_restore_current_thread::scoped_restore_current_thread):
+	Save the language as well.  Use save_selected_frame.
+
+Change-Id: I73fd1cfc40d8513c28e5596383b7ecd8bcfe700f
+
+diff --git a/gdb/blockframe.c b/gdb/blockframe.c
+--- a/gdb/blockframe.c
++++ b/gdb/blockframe.c
+@@ -464,14 +464,10 @@ find_gnu_ifunc_target_type (CORE_ADDR resolver_funaddr)
+ struct frame_info *
+ block_innermost_frame (const struct block *block)
+ {
+-  struct frame_info *frame;
+-
+   if (block == NULL)
+     return NULL;
+ 
+-  frame = get_selected_frame_if_set ();
+-  if (frame == NULL)
+-    frame = get_current_frame ();
++  frame_info *frame = get_selected_frame ();
+   while (frame != NULL)
+     {
+       const struct block *frame_block = get_frame_block (frame, NULL);
+diff --git a/gdb/frame.c b/gdb/frame.c
+--- a/gdb/frame.c
++++ b/gdb/frame.c
+@@ -317,17 +317,15 @@ frame_stash_invalidate (void)
+ /* See frame.h  */
+ scoped_restore_selected_frame::scoped_restore_selected_frame ()
+ {
+-  m_fid = get_frame_id (get_selected_frame (NULL));
++  m_lang = current_language->la_language;
++  save_selected_frame (&m_fid, &m_level);
+ }
+ 
+ /* See frame.h  */
+ scoped_restore_selected_frame::~scoped_restore_selected_frame ()
+ {
+-  frame_info *frame = frame_find_by_id (m_fid);
+-  if (frame == NULL)
+-    warning (_("Unable to restore previously selected frame."));
+-  else
+-    select_frame (frame);
++  restore_selected_frame (m_fid, m_level);
++  set_language (m_lang);
+ }
+ 
+ /* Flag to control debugging.  */
+@@ -1685,10 +1683,63 @@ get_current_frame (void)
+ }
+ 
+ /* The "selected" stack frame is used by default for local and arg
+-   access.  May be zero, for no selected frame.  */
+-
++   access.
++
++   The "single source of truth" for the selected frame is the
++   SELECTED_FRAME_ID / SELECTED_FRAME_LEVEL pair.
++
++   Frame IDs can be saved/restored across reinitializing the frame
++   cache, while frame_info pointers can't (frame_info objects are
++   invalidated).  If we know the corresponding frame_info object, it
++   is cached in SELECTED_FRAME.
++
++   If SELECTED_FRAME_ID / SELECTED_FRAME_LEVEL are null_frame_id / -1,
++   and the target has stack and is stopped, the selected frame is the
++   current (innermost) frame.  This means that SELECTED_FRAME_LEVEL is
++   never 0 and SELECTED_FRAME_ID is never the ID of the innermost
++   frame.
++
++   If SELECTED_FRAME_ID / SELECTED_FRAME_LEVEL are null_frame_id / -1,
++   and the target has no stack or is executing, then there's no
++   selected frame.  */
++static frame_id selected_frame_id = null_frame_id;
++static int selected_frame_level = -1;
++
++/* The cached frame_info object pointing to the selected frame.
++   Looked up on demand by get_selected_frame.  */
+ static struct frame_info *selected_frame;
+ 
++/* See frame.h.  */
++
++void
++save_selected_frame (frame_id *frame_id, int *frame_level)
++  noexcept
++{
++  *frame_id = selected_frame_id;
++  *frame_level = selected_frame_level;
++}
++
++/* See frame.h.  */
++
++void
++restore_selected_frame (frame_id frame_id, int frame_level)
++  noexcept
++{
++  /* save_selected_frame never returns level == 0, so we shouldn't see
++     it here either.  */
++  gdb_assert (frame_level != 0);
++
++  /* FRAME_ID can be null_frame_id only IFF frame_level is -1.  */
++  gdb_assert ((frame_level == -1 && !frame_id_p (frame_id))
++	      || (frame_level != -1 && frame_id_p (frame_id)));
++
++  selected_frame_id = frame_id;
++  selected_frame_level = frame_level;
++
++  /* Will be looked up later by get_selected_frame.  */
++  selected_frame = nullptr;
++}
++
+ bool
+ has_stack_frames ()
+ {
+@@ -1715,9 +1766,7 @@ has_stack_frames ()
+   return true;
+ }
+ 
+-/* Return the selected frame.  Always non-NULL (unless there isn't an
+-   inferior sufficient for creating a frame) in which case an error is
+-   thrown.  */
++/* See frame.h.  */
+ 
+ struct frame_info *
+ get_selected_frame (const char *message)
+@@ -1726,24 +1775,14 @@ get_selected_frame (const char *message)
+     {
+       if (message != NULL && !has_stack_frames ())
+ 	error (("%s"), message);
+-      /* Hey!  Don't trust this.  It should really be re-finding the
+-	 last selected frame of the currently selected thread.  This,
+-	 though, is better than nothing.  */
+-      select_frame (get_current_frame ());
++
++      lookup_selected_frame (selected_frame_id, selected_frame_level);
+     }
+   /* There is always a frame.  */
+   gdb_assert (selected_frame != NULL);
+   return selected_frame;
+ }
+ 
+-/* If there is a selected frame, return it.  Otherwise, return NULL.  */
+-
+-struct frame_info *
+-get_selected_frame_if_set (void)
+-{
+-  return selected_frame;
+-}
+-
+ /* This is a variant of get_selected_frame() which can be called when
+    the inferior does not have a frame; in that case it will return
+    NULL instead of calling error().  */
+@@ -1756,12 +1795,42 @@ deprecated_safe_get_selected_frame (void)
+   return get_selected_frame (NULL);
+ }
+ 
+-/* Select frame FI (or NULL - to invalidate the current frame).  */
++/* Select frame FI (or NULL - to invalidate the selected frame).  */
+ 
+ void
+ select_frame (struct frame_info *fi)
+ {
+   selected_frame = fi;
++  selected_frame_level = frame_relative_level (fi);
++  if (selected_frame_level == 0)
++    {
++      /* Treat the current frame especially -- we want to always
++	 save/restore it without warning, even if the frame ID changes
++	 (see lookup_selected_frame).  E.g.:
++
++	  // The current frame is selected, the target had just stopped.
++	  {
++	    scoped_restore_selected_frame restore_frame;
++	    some_operation_that_changes_the_stack ();
++	  }
++	  // scoped_restore_selected_frame's dtor runs, but the
++	  // original frame_id can't be found.  No matter whether it
++	  // is found or not, we still end up with the now-current
++	  // frame selected.  Warning in lookup_selected_frame in this
++	  // case seems pointless.
++
++	 Also get_frame_id may access the target's registers/memory,
++	 and thus skipping get_frame_id optimizes the common case.
++
++	 Saving the selected frame this way makes get_selected_frame
++	 and restore_current_frame return/re-select whatever frame is
++	 the innermost (current) then.  */
++      selected_frame_level = -1;
++      selected_frame_id = null_frame_id;
++    }
++  else
++    selected_frame_id = get_frame_id (fi);
++
+   /* NOTE: cagney/2002-05-04: FI can be NULL.  This occurs when the
+      frame is being invalidated.  */
+ 
+diff --git a/gdb/frame.h b/gdb/frame.h
+--- a/gdb/frame.h
++++ b/gdb/frame.h
+@@ -186,8 +186,14 @@ class scoped_restore_selected_frame
+ 
+ private:
+ 
+-  /* The ID of the previously selected frame.  */
++  /* The ID and level of the previously selected frame.  */
+   struct frame_id m_fid;
++  int m_level;
++
++  /* Save/restore the language as well, because selecting a frame
++     changes the current language to the frame's language if "set
++     language auto".  */
++  enum language m_lang;
+ };
+ 
+ /* Methods for constructing and comparing Frame IDs.  */
+@@ -316,24 +322,49 @@ extern bool has_stack_frames ();
+    modifies the target invalidating the frame cache).  */
+ extern void reinit_frame_cache (void);
+ 
+-/* On demand, create the selected frame and then return it.  If the
+-   selected frame can not be created, this function prints then throws
+-   an error.  When MESSAGE is non-NULL, use it for the error message,
++/* Return the selected frame.  Always returns non-NULL.  If there
++   isn't an inferior sufficient for creating a frame, an error is
++   thrown.  When MESSAGE is non-NULL, use it for the error message,
+    otherwise use a generic error message.  */
+ /* FIXME: cagney/2002-11-28: At present, when there is no selected
+    frame, this function always returns the current (inner most) frame.
+    It should instead, when a thread has previously had its frame
+    selected (but not resumed) and the frame cache invalidated, find
+    and then return that thread's previously selected frame.  */
+-extern struct frame_info *get_selected_frame (const char *message);
+-
+-/* If there is a selected frame, return it.  Otherwise, return NULL.  */
+-extern struct frame_info *get_selected_frame_if_set (void);
++extern struct frame_info *get_selected_frame (const char *message = nullptr);
+ 
+-/* Select a specific frame.  NULL, apparently implies re-select the
+-   inner most frame.  */
++/* Select a specific frame.  NULL implies re-select the inner most
++   frame.  */
+ extern void select_frame (struct frame_info *);
+ 
++/* Save the frame ID and frame level of the selected frame in FRAME_ID
++   and FRAME_LEVEL, to be restored later with restore_selected_frame.
++
++   This is preferred over getting the same info out of
++   get_selected_frame directly because this function does not create
++   the selected-frame's frame_info object if it hasn't been created
++   yet, and thus is more efficient and doesn't throw.  */
++extern void save_selected_frame (frame_id *frame_id, int *frame_level)
++  noexcept;
++
++/* Restore selected frame as saved with save_selected_frame.
++
++   Does not try to find the corresponding frame_info object.  Instead
++   the next call to get_selected_frame will look it up and cache the
++   result.
++
++   This function does not throw.  It is designed to be safe to called
++   from the destructors of RAII types.  */
++extern void restore_selected_frame (frame_id frame_id, int frame_level)
++  noexcept;
++
++/* Lookup the frame_info object for the selected frame FRAME_ID /
++   FRAME_LEVEL and cache the result.
++
++   If FRAME_LEVEL > 0 and the originally selected frame isn't found,
++   warn and select the innermost (current) frame.  */
++extern void lookup_selected_frame (frame_id frame_id, int frame_level);
++
+ /* Given a FRAME, return the next (more inner, younger) or previous
+    (more outer, older) frame.  */
+ extern struct frame_info *get_prev_frame (struct frame_info *);
+diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
+--- a/gdb/gdbthread.h
++++ b/gdb/gdbthread.h
+@@ -673,6 +673,10 @@ class scoped_restore_current_thread
+   frame_id m_selected_frame_id;
+   int m_selected_frame_level;
+   bool m_was_stopped;
++  /* Save/restore the language as well, because selecting a frame
++     changes the current language to the frame's language if "set
++     language auto".  */
++  enum language m_lang;
+ };
+ 
+ /* Returns a pointer into the thread_info corresponding to
+diff --git a/gdb/infrun.c b/gdb/infrun.c
+--- a/gdb/infrun.c
++++ b/gdb/infrun.c
+@@ -9017,8 +9017,10 @@ struct infcall_control_state
+   enum stop_stack_kind stop_stack_dummy = STOP_NONE;
+   int stopped_by_random_signal = 0;
+ 
+-  /* ID if the selected frame when the inferior function call was made.  */
++  /* ID and level of the selected frame when the inferior function
++     call was made.  */
+   struct frame_id selected_frame_id {};
++  int selected_frame_level = -1;
+ };
+ 
+ /* Save all of the information associated with the inferior<==>gdb
+@@ -9047,27 +9049,12 @@ save_infcall_control_state ()
+   inf_status->stop_stack_dummy = stop_stack_dummy;
+   inf_status->stopped_by_random_signal = stopped_by_random_signal;
+ 
+-  inf_status->selected_frame_id = get_frame_id (get_selected_frame (NULL));
++  save_selected_frame (&inf_status->selected_frame_id,
++		       &inf_status->selected_frame_level);
+ 
+   return inf_status;
+ }
+ 
+-static void
+-restore_selected_frame (const frame_id &fid)
+-{
+-  frame_info *frame = frame_find_by_id (fid);
+-
+-  /* If inf_status->selected_frame_id is NULL, there was no previously
+-     selected frame.  */
+-  if (frame == NULL)
+-    {
+-      warning (_("Unable to restore previously selected frame."));
+-      return;
+-    }
+-
+-  select_frame (frame);
+-}
+-
+ /* Restore inferior session state to INF_STATUS.  */
+ 
+ void
+@@ -9095,21 +9082,8 @@ restore_infcall_control_state (struct infcall_control_state *inf_status)
+ 
+   if (target_has_stack)
+     {
+-      /* The point of the try/catch is that if the stack is clobbered,
+-         walking the stack might encounter a garbage pointer and
+-         error() trying to dereference it.  */
+-      try
+-	{
+-	  restore_selected_frame (inf_status->selected_frame_id);
+-	}
+-      catch (const gdb_exception_error &ex)
+-	{
+-	  exception_fprintf (gdb_stderr, ex,
+-			     "Unable to restore previously selected frame:\n");
+-	  /* Error in restoring the selected frame.  Select the
+-	     innermost frame.  */
+-	  select_frame (get_current_frame ());
+-	}
++      restore_selected_frame (inf_status->selected_frame_id,
++			      inf_status->selected_frame_level);
+     }
+ 
+   delete inf_status;
+diff --git a/gdb/stack.c b/gdb/stack.c
+--- a/gdb/stack.c
++++ b/gdb/stack.c
+@@ -1842,9 +1842,9 @@ trailing_outermost_frame (int count)
+ static void
+ select_frame_command_core (struct frame_info *fi, bool ignored)
+ {
+-  struct frame_info *prev_frame = get_selected_frame_if_set ();
++  frame_info *prev_frame = get_selected_frame ();
+   select_frame (fi);
+-  if (get_selected_frame_if_set () != prev_frame)
++  if (get_selected_frame () != prev_frame)
+     gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+ }
+ 
+@@ -1863,10 +1863,9 @@ select_frame_for_mi (struct frame_info *fi)
+ static void
+ frame_command_core (struct frame_info *fi, bool ignored)
+ {
+-  struct frame_info *prev_frame = get_selected_frame_if_set ();
+-
++  frame_info *prev_frame = get_selected_frame ();
+   select_frame (fi);
+-  if (get_selected_frame_if_set () != prev_frame)
++  if (get_selected_frame () != prev_frame)
+     gdb::observers::user_selected_context_changed.notify (USER_SELECTED_FRAME);
+   else
+     print_selected_thread_frame (current_uiout, USER_SELECTED_FRAME);
+diff --git a/gdb/thread.c b/gdb/thread.c
+--- a/gdb/thread.c
++++ b/gdb/thread.c
+@@ -1325,20 +1325,26 @@ switch_to_thread (process_stratum_target *proc_target, ptid_t ptid)
+   switch_to_thread (thr);
+ }
+ 
+-static void
+-restore_selected_frame (struct frame_id a_frame_id, int frame_level)
++/* See frame.h.  */
++
++void
++lookup_selected_frame (struct frame_id a_frame_id, int frame_level)
+ {
+   struct frame_info *frame = NULL;
+   int count;
+ 
+-  /* This means there was no selected frame.  */
++  /* This either means there was no selected frame, or the selected
++     frame was the current frame.  In either case, select the current
++     frame.  */
+   if (frame_level == -1)
+     {
+-      select_frame (NULL);
++      select_frame (get_current_frame ());
+       return;
+     }
+ 
+-  gdb_assert (frame_level >= 0);
++  /* select_frame never saves 0 in SELECTED_FRAME_LEVEL, so we
++     shouldn't see it here.  */
++  gdb_assert (frame_level > 0);
+ 
+   /* Restore by level first, check if the frame id is the same as
+      expected.  If that fails, try restoring by frame id.  If that
+@@ -1409,64 +1415,28 @@ scoped_restore_current_thread::restore ()
+       && target_has_stack
+       && target_has_memory)
+     restore_selected_frame (m_selected_frame_id, m_selected_frame_level);
++
++  set_language (m_lang);
+ }
+ 
+ scoped_restore_current_thread::~scoped_restore_current_thread ()
+ {
+   if (!m_dont_restore)
+-    {
+-      try
+-	{
+-	  restore ();
+-	}
+-      catch (const gdb_exception &ex)
+-	{
+-	  /* We're in a dtor, there's really nothing else we can do
+-	     but swallow the exception.  */
+-	}
+-    }
++    restore ();
+ }
+ 
+ scoped_restore_current_thread::scoped_restore_current_thread ()
+ {
+   m_inf = inferior_ref::new_reference (current_inferior ());
+ 
++  m_lang = current_language->la_language;
++
+   if (inferior_ptid != null_ptid)
+     {
+       m_thread = thread_info_ref::new_reference (inferior_thread ());
+ 
+-      struct frame_info *frame;
+-
+       m_was_stopped = m_thread->state == THREAD_STOPPED;
+-      if (m_was_stopped
+-	  && target_has_registers
+-	  && target_has_stack
+-	  && target_has_memory)
+-	{
+-	  /* When processing internal events, there might not be a
+-	     selected frame.  If we naively call get_selected_frame
+-	     here, then we can end up reading debuginfo for the
+-	     current frame, but we don't generally need the debuginfo
+-	     at this point.  */
+-	  frame = get_selected_frame_if_set ();
+-	}
+-      else
+-	frame = NULL;
+-
+-      try
+-	{
+-	  m_selected_frame_id = get_frame_id (frame);
+-	  m_selected_frame_level = frame_relative_level (frame);
+-	}
+-      catch (const gdb_exception_error &ex)
+-	{
+-	  m_selected_frame_id = null_frame_id;
+-	  m_selected_frame_level = -1;
+-
+-	  /* Better let this propagate.  */
+-	  if (ex.error == TARGET_CLOSE_ERROR)
+-	    throw;
+-	}
++      save_selected_frame (&m_selected_frame_id, &m_selected_frame_level);
+     }
+ }
+ 
diff --git a/gdb-rhbz1909902-frame_id_p-assert-2.patch b/gdb-rhbz1909902-frame_id_p-assert-2.patch
new file mode 100644
index 0000000..d804bbe
--- /dev/null
+++ b/gdb-rhbz1909902-frame_id_p-assert-2.patch
@@ -0,0 +1,169 @@
+From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
+From: Pedro Alves <pedro@palves.net>
+Date: Sat, 31 Oct 2020 00:27:18 +0000
+Subject: gdb-rhbz1909902-frame_id_p-assert-2.patch
+
+;; Backport patch #2 which fixes a frame_id_p assertion failure (RH BZ 1909902).
+
+Fix frame cycle detection
+
+The recent commit to make scoped_restore_current_thread's cdtors
+exception free regressed gdb.base/eh_return.exp:
+
+  Breakpoint 1, 0x00000000004012bb in eh2 (gdb/frame.c:641: internal-error: frame_id get_frame_id(frame_info*): Assertion `stashed' failed.
+  A problem internal to GDB has been detected,
+  further debugging may prove unreliable.
+  Quit this debugging session? (y or n) FAIL: gdb.base/eh_return.exp: hit breakpoint (GDB internal error)
+
+That testcase uses __builtin_eh_return and, before the regression, the
+backtrace at eh2 looked like this:
+
+ (gdb) bt
+ #0  0x00000000004006eb in eh2 (p=0x4006ec <continuation>) at src/gdb/testsuite/gdb.base/eh_return.c:54
+ Backtrace stopped: previous frame identical to this frame (corrupt stack?)
+
+That "previous frame identical to this frame" is caught by the cycle
+detection based on frame id.
+
+The assertion failing is this one:
+
+ 638           /* Since this is the first frame in the chain, this should
+ 639              always succeed.  */
+ 640           bool stashed = frame_stash_add (fi);
+ 641           gdb_assert (stashed);
+
+originally added by
+
+  commit f245535cf583ae4ca13b10d47b3c7d3334593ece
+  Author:     Pedro Alves <palves@redhat.com>
+  AuthorDate: Mon Sep 5 18:41:38 2016 +0100
+
+      Fix PR19927: Avoid unwinder recursion if sniffer uses calls parse_and_eval
+
+The assertion is failing because frame #1's frame id was stashed
+before the id of frame #0 is stashed.  The frame id of frame #1 was
+stashed here:
+
+ (top-gdb) bt
+ #0  frame_stash_add (frame=0x1e24c90) at src/gdb/frame.c:276
+ #1  0x0000000000669c1b in get_prev_frame_if_no_cycle (this_frame=0x19f8370) at src/gdb/frame.c:2120
+ #2  0x000000000066a339 in get_prev_frame_always_1 (this_frame=0x19f8370) at src/gdb/frame.c:2303
+ #3  0x000000000066a360 in get_prev_frame_always (this_frame=0x19f8370) at src/gdb/frame.c:2319
+ #4  0x000000000066b56c in get_frame_unwind_stop_reason (frame=0x19f8370) at src/gdb/frame.c:3028
+ #5  0x000000000059f929 in dwarf2_frame_cfa (this_frame=0x19f8370) at src/gdb/dwarf2/frame.c:1462
+ #6  0x00000000005ce434 in dwarf_evaluate_loc_desc::get_frame_cfa (this=0x7fffffffc070) at src/gdb/dwarf2/loc.c:666
+ #7  0x00000000005989a9 in dwarf_expr_context::execute_stack_op (this=0x7fffffffc070, op_ptr=0x1b2a053 "\364\003", op_end=0x1b2a053 "\364\003") at src/gdb/dwarf2/expr.c:1161
+ #8  0x0000000000596af6 in dwarf_expr_context::eval (this=0x7fffffffc070, addr=0x1b2a052 "\234\364\003", len=1) at src/gdb/dwarf2/expr.c:303
+ #9  0x0000000000597b4e in dwarf_expr_context::execute_stack_op (this=0x7fffffffc070, op_ptr=0x1b2a063 "", op_end=0x1b2a063 "") at src/gdb/dwarf2/expr.c:865
+ #10 0x0000000000596af6 in dwarf_expr_context::eval (this=0x7fffffffc070, addr=0x1b2a061 "\221X", len=2) at src/gdb/dwarf2/expr.c:303
+ #11 0x00000000005c8b5a in dwarf2_evaluate_loc_desc_full (type=0x1b564d0, frame=0x19f8370, data=0x1b2a061 "\221X", size=2, per_cu=0x1b28760, per_objfile=0x1a84930, subobj_type=0x1b564d0, subobj_byte_offset=0) at src/gdb/dwarf2/loc.c:2260
+ #12 0x00000000005c9243 in dwarf2_evaluate_loc_desc (type=0x1b564d0, frame=0x19f8370, data=0x1b2a061 "\221X", size=2, per_cu=0x1b28760, per_objfile=0x1a84930) at src/gdb/dwarf2/loc.c:2444
+ #13 0x00000000005cb769 in locexpr_read_variable (symbol=0x1b59840, frame=0x19f8370) at src/gdb/dwarf2/loc.c:3687
+ #14 0x0000000000663137 in language_defn::read_var_value (this=0x122ea60 <c_language_defn>, var=0x1b59840, var_block=0x0, frame=0x19f8370) at src/gdb/findvar.c:618
+ #15 0x0000000000663c3b in read_var_value (var=0x1b59840, var_block=0x0, frame=0x19f8370) at src/gdb/findvar.c:822
+ #16 0x00000000008c7d9f in read_frame_arg (fp_opts=..., sym=0x1b59840, frame=0x19f8370, argp=0x7fffffffc470, entryargp=0x7fffffffc490) at src/gdb/stack.c:542
+ #17 0x00000000008c89cd in print_frame_args (fp_opts=..., func=0x1b597c0, frame=0x19f8370, num=-1, stream=0x1aba860) at src/gdb/stack.c:890
+ #18 0x00000000008c9bf8 in print_frame (fp_opts=..., frame=0x19f8370, print_level=0, print_what=SRC_AND_LOC, print_args=1, sal=...) at src/gdb/stack.c:1394
+ #19 0x00000000008c92b9 in print_frame_info (fp_opts=..., frame=0x19f8370, print_level=0, print_what=SRC_AND_LOC, print_args=1, set_current_sal=1) at src/gdb/stack.c:1119
+ #20 0x00000000008c75f0 in print_stack_frame (frame=0x19f8370, print_level=0, print_what=SRC_AND_LOC, set_current_sal=1) at src/gdb/stack.c:366
+ #21 0x000000000070250b in print_stop_location (ws=0x7fffffffc9e0) at src/gdb/infrun.c:8110
+ #22 0x0000000000702569 in print_stop_event (uiout=0x1a8b9e0, displays=true) at src/gdb/infrun.c:8126
+ #23 0x000000000096d04b in tui_on_normal_stop (bs=0x1bcd1c0, print_frame=1) at src/gdb/tui/tui-interp.c:98
+ ...
+
+Before the commit to make scoped_restore_current_thread's cdtors
+exception free, scoped_restore_current_thread's dtor would call
+get_frame_id on the selected frame, and we use
+scoped_restore_current_thread pervasively.  That had the side effect
+of stashing the frame id of frame #0 before reaching the path shown in
+the backtrace.  I.e., the frame id of frame #0 happened to be stashed
+before the frame id of frame #1.  But that was by chance, not by
+design.
+
+This commit:
+
+  commit 256ae5dbc73d1348850f86ee77a0dc3b04bc7cc0
+  Author:     Kevin Buettner <kevinb@redhat.com>
+  AuthorDate: Mon Oct 31 12:47:42 2016 -0700
+
+      Stash frame id of current frame before stashing frame id for previous frame
+
+Fixed a similar problem, by making sure get_prev_frame computes the
+frame id of the current frame before unwinding the previous frame, so
+that the cycle detection works properly.  That fix misses the scenario
+we're now running against, because if you notice, the backtrace above
+shows that frame #4 calls get_prev_frame_always, not get_prev_frame.
+I.e., nothing is calling get_frame_id on the current frame.
+
+The fix here is to move Kevin's fix down from get_prev_frame to
+get_prev_frame_always.  Or actually, a bit further down to
+get_prev_frame_always_1 -- note that inline_frame_this_id calls
+get_prev_frame_always, so we need to be careful to avoid recursion in
+that scenario.
+
+gdb/ChangeLog:
+
+	* frame.c (get_prev_frame): Move get_frame_id call from here ...
+	(get_prev_frame_always_1): ... to here.
+	* inline-frame.c (inline_frame_this_id): Mention
+	get_prev_frame_always_1 in comment.
+
+Change-Id: Id960c98ab2d072c48a436c3eb160cc4b2a5cfd1d
+
+diff --git a/gdb/frame.c b/gdb/frame.c
+--- a/gdb/frame.c
++++ b/gdb/frame.c
+@@ -2133,6 +2133,23 @@ get_prev_frame_always_1 (struct frame_info *this_frame)
+   if (get_frame_type (this_frame) == INLINE_FRAME)
+     return get_prev_frame_if_no_cycle (this_frame);
+ 
++  /* If this_frame is the current frame, then compute and stash its
++     frame id prior to fetching and computing the frame id of the
++     previous frame.  Otherwise, the cycle detection code in
++     get_prev_frame_if_no_cycle() will not work correctly.  When
++     get_frame_id() is called later on, an assertion error will be
++     triggered in the event of a cycle between the current frame and
++     its previous frame.
++
++     Note we do this after the INLINE_FRAME check above.  That is
++     because the inline frame's frame id computation needs to fetch
++     the frame id of its previous real stack frame.  I.e., we need to
++     avoid recursion in that case.  This is OK since we're sure the
++     inline frame won't create a cycle with the real stack frame.  See
++     inline_frame_this_id.  */
++  if (this_frame->level == 0)
++    get_frame_id (this_frame);
++
+   /* Check that this frame is unwindable.  If it isn't, don't try to
+      unwind to the prev frame.  */
+   this_frame->stop_reason
+@@ -2410,16 +2427,6 @@ get_prev_frame (struct frame_info *this_frame)
+      something should be calling get_selected_frame() or
+      get_current_frame().  */
+   gdb_assert (this_frame != NULL);
+-  
+-  /* If this_frame is the current frame, then compute and stash
+-     its frame id prior to fetching and computing the frame id of the
+-     previous frame.  Otherwise, the cycle detection code in
+-     get_prev_frame_if_no_cycle() will not work correctly.  When
+-     get_frame_id() is called later on, an assertion error will
+-     be triggered in the event of a cycle between the current
+-     frame and its previous frame.  */
+-  if (this_frame->level == 0)
+-    get_frame_id (this_frame);
+ 
+   frame_pc_p = get_frame_pc_if_available (this_frame, &frame_pc);
+ 
+diff --git a/gdb/inline-frame.c b/gdb/inline-frame.c
+--- a/gdb/inline-frame.c
++++ b/gdb/inline-frame.c
+@@ -161,7 +161,8 @@ inline_frame_this_id (struct frame_info *this_frame,
+      real frame's this_id method.  So we must call
+      get_prev_frame_always.  Because we are inlined into some
+      function, there must be previous frames, so this is safe - as
+-     long as we're careful not to create any cycles.  */
++     long as we're careful not to create any cycles.  See related
++     comments in get_prev_frame_always_1.  */
+   *this_id = get_frame_id (get_prev_frame_always (this_frame));
+ 
+   /* We need a valid frame ID, so we need to be based on a valid
diff --git a/gdb.spec b/gdb.spec
index 5ef511d..19cd878 100644
--- a/gdb.spec
+++ b/gdb.spec
@@ -37,7 +37,7 @@ Version: 10.1
 
 # The release always contains a leading reserved number, start it at 1.
 # `upstream' is not a part of `name' to stay fully rpm dependencies compatible for the testing.
-Release: 13%{?dist}
+Release: 14%{?dist}
 
 License: GPLv3+ and GPLv3+ with exceptions and GPLv2+ and GPLv2+ with exceptions and GPL+ and LGPLv2+ and LGPLv3+ and BSD and Public Domain and GFDL
 # Do not provide URL for snapshots as the file lasts there only for 2 days.
@@ -1195,6 +1195,10 @@ fi
 %endif
 
 %changelog
+* Fri Mar 05 2021 Kevin Buettner <kevinb@redhat.com> - 10.1-14
+- Backport patches which fix frame_id_p assertion failure (RHBZ 1909902,
+  Pedro Alves).
+
 * Fri Mar  5 2021 Jan Kratochvil <jan.kratochvil@redhat.com> - 10.1-13
 - Drop gdb-vla-intel-fortran-vla-strings.patch as it was still regressing the
   testsuite.