diff --git a/gcc/analyzer/region-model-impl-calls.cc b/gcc/analyzer/region-model-impl-calls.cc
index 8eebd122d42..9f1ae020f4f 100644
--- a/gcc/analyzer/region-model-impl-calls.cc
+++ b/gcc/analyzer/region-model-impl-calls.cc
@@ -1019,13 +1019,69 @@ region_model::impl_call_strcpy (const call_details &cd)
   const svalue *dest_sval = cd.get_arg_svalue (0);
   const region *dest_reg = deref_rvalue (dest_sval, cd.get_arg_tree (0),
 					 cd.get_ctxt ());
+  const svalue *src_sval = cd.get_arg_svalue (1);
+  const region *src_reg = deref_rvalue (src_sval, cd.get_arg_tree (1),
+					cd.get_ctxt ());
+  const svalue *src_contents_sval = get_store_value (src_reg,
+						     cd.get_ctxt ());
 
   cd.maybe_set_lhs (dest_sval);
 
-  check_region_for_write (dest_reg, cd.get_ctxt ());
+  /* Try to get the string size if SRC_REG is a string_region.  */
+  const svalue *copied_bytes_sval = get_string_size (src_reg);
+  /* Otherwise, check if the contents of SRC_REG is a string.  */
+  if (copied_bytes_sval->get_kind () == SK_UNKNOWN)
+    copied_bytes_sval = get_string_size (src_contents_sval);
+
+  const region *sized_dest_reg
+    = m_mgr->get_sized_region (dest_reg, NULL_TREE, copied_bytes_sval);
+  set_value (sized_dest_reg, src_contents_sval, cd.get_ctxt ());
+}
+
+/* Handle the on_call_pre part of "strncpy" and "__builtin_strncpy_chk".  */
 
-  /* For now, just mark region's contents as unknown.  */
-  mark_region_as_unknown (dest_reg, cd.get_uncertainty ());
+void
+region_model::impl_call_strncpy (const call_details &cd)
+{
+  const svalue *dest_sval = cd.get_arg_svalue (0);
+  const region *dest_reg = deref_rvalue (dest_sval, cd.get_arg_tree (0),
+					 cd.get_ctxt ());
+  const svalue *src_sval = cd.get_arg_svalue (1);
+  const region *src_reg = deref_rvalue (src_sval, cd.get_arg_tree (1),
+					cd.get_ctxt ());
+  const svalue *src_contents_sval = get_store_value (src_reg,
+						     cd.get_ctxt ());
+  const svalue *num_bytes_sval = cd.get_arg_svalue (2);
+
+  cd.maybe_set_lhs (dest_sval);
+
+  const svalue *string_size_sval = get_string_size (src_reg);
+  if (string_size_sval->get_kind () == SK_UNKNOWN)
+    string_size_sval = get_string_size (src_contents_sval);
+
+  /* strncpy copies until a zero terminator is reached or n bytes were copied.
+     Determine the lesser of both here.  */
+  tristate ts = eval_condition (string_size_sval, LT_EXPR, num_bytes_sval);
+  const svalue *copied_bytes_sval;
+  switch (ts.get_value ())
+    {
+      case tristate::TS_TRUE:
+	copied_bytes_sval = string_size_sval;
+	break;
+      case tristate::TS_FALSE:
+	copied_bytes_sval = num_bytes_sval;
+	break;
+      case tristate::TS_UNKNOWN:
+	copied_bytes_sval
+	  = m_mgr->get_or_create_unknown_svalue (size_type_node);
+	break;
+      default:
+	gcc_unreachable ();
+    }
+
+  const region *sized_dest_reg = m_mgr->get_sized_region (dest_reg, NULL_TREE,
+							  copied_bytes_sval);
+  set_value (sized_dest_reg, src_contents_sval, cd.get_ctxt ());
 }
 
 /* Handle the on_call_pre part of "strlen".  */
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index ec29be259b5..c87c79e3f1b 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -1858,6 +1858,10 @@ region_model::on_call_pre (const gcall *call, region_model_context *ctxt,
 	  case BUILT_IN_STRCPY_CHK:
 	    impl_call_strcpy (cd);
 	    return false;
+	  case BUILT_IN_STRNCPY:
+	  case BUILT_IN_STRNCPY_CHK:
+	    impl_call_strncpy (cd);
+	    return false;
 	  case BUILT_IN_STRLEN:
 	    impl_call_strlen (cd);
 	    return false;
@@ -3218,6 +3222,35 @@ region_model::get_capacity (const region *reg) const
   return m_mgr->get_or_create_unknown_svalue (sizetype);
 }
 
+/* Return the string size, including the 0-terminator, if SVAL is a
+   constant_svalue holding a string.  Otherwise, return an unknown_svalue.  */
+
+const svalue *
+region_model::get_string_size (const svalue *sval) const
+{
+  tree cst = sval->maybe_get_constant ();
+  if (!cst || TREE_CODE (cst) != STRING_CST)
+    return m_mgr->get_or_create_unknown_svalue (size_type_node);
+
+  tree out = build_int_cst (size_type_node, TREE_STRING_LENGTH (cst));
+  return m_mgr->get_or_create_constant_svalue (out);
+}
+
+/* Return the string size, including the 0-terminator, if REG is a
+   string_region.  Otherwise, return an unknown_svalue.  */
+
+const svalue *
+region_model::get_string_size (const region *reg) const
+{
+  const string_region *str_reg = dyn_cast <const string_region *> (reg);
+  if (!str_reg)
+    return m_mgr->get_or_create_unknown_svalue (size_type_node);
+
+  tree cst = str_reg->get_string_cst ();
+  tree out = build_int_cst (size_type_node, TREE_STRING_LENGTH (cst));
+  return m_mgr->get_or_create_constant_svalue (out);
+}
+
 /* If CTXT is non-NULL, use it to warn about any problems accessing REG,
    using DIR to determine if this access is a read or write.  */
 
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index 7ce832f6ce4..a6a92d46d91 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -634,6 +634,7 @@ class region_model
   void impl_call_realloc (const call_details &cd);
   void impl_call_strchr (const call_details &cd);
   void impl_call_strcpy (const call_details &cd);
+  void impl_call_strncpy (const call_details &cd);
   void impl_call_strlen (const call_details &cd);
   void impl_call_operator_new (const call_details &cd);
   void impl_call_operator_delete (const call_details &cd);
@@ -793,6 +794,9 @@ class region_model
 
   const svalue *get_capacity (const region *reg) const;
 
+  const svalue *get_string_size (const svalue *sval) const;
+  const svalue *get_string_size (const region *reg) const;
+
   /* Implemented in sm-malloc.cc  */
   void on_realloc_with_move (const call_details &cd,
 			     const svalue *old_ptr_sval,
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-4.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-4.c
new file mode 100644
index 00000000000..382f0fb5ef4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-4.c
@@ -0,0 +1,122 @@
+/* { dg-additional-options "-Wno-stringop-overflow -Wno-stringop-truncation" } */
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+/* Wanalyzer-out-of-bounds tests for str(n)py-related overflows.
+  
+   The intra-procedural tests are all catched by Wstringop-overflow.
+   The inter-procedural out-of-bounds are only found by the analyzer.  */
+
+void test1 (void)
+{
+  char dst[5];
+  strcpy (dst, "Hello"); /* { dg-line test1 } */
+
+  /* { dg-warning "overflow" "warning" { target *-*-* } test1 } */
+  /* { dg-message "dst" "note" { target *-*-* } test1 } */
+}
+
+void test2 (void)
+{
+  char dst[6];
+  strcpy (dst, "Hello");
+}
+
+void test3 (void)
+{
+  char dst[5];
+  strncpy (dst, "Hello", 6); /* { dg-line test3 } */
+
+  /* { dg-warning "overflow" "warning" { target *-*-* } test3 } */
+  /* { dg-message "dst" "note" { target *-*-* } test3 } */
+}
+
+void test4 (void)
+{
+  char dst[5];
+  strncpy (dst, "Hello", 5);
+}
+
+void test5 (void)
+{
+  char *src = "Hello";
+  char dst[5];
+  strcpy (dst, src); /* { dg-line test5 } */
+
+  /* { dg-warning "overflow" "warning" { target *-*-* } test5 } */
+  /* { dg-message "dst" "note" { target *-*-* } test5 } */
+}
+
+void test6 (void)
+{
+  char *src = "Hello";
+  char dst[6];
+  strcpy (dst, src);
+}
+
+void test7 (void)
+{
+  int n = 6;
+  char *src = "Hello";
+  char dst[5];
+  strncpy (dst, src, n); /* { dg-line test7 } */
+
+  /* { dg-warning "overflow" "warning" { target *-*-* } test7 } */
+  /* { dg-message "dst" "note" { target *-*-* } test7 } */
+}
+
+void test8 (void)
+{
+  int n = 5;
+  char *src = "Hello";
+  char dst[n];
+  strncpy (dst, src, n);
+}
+
+char *return_hello (void)
+{
+  return "hello";
+}
+
+void test9 (void)
+{
+  char *str = return_hello ();
+  if (!str)
+    return;
+  char dst[5];
+  strcpy (dst, str); /* { dg-line test9 } */
+
+  /* { dg-warning "overflow" "warning" { target *-*-* } test9 } */
+  /* { dg-message "dst" "note" { target *-*-* } test9 } */
+}
+
+void test10 (void)
+{
+  char *str = return_hello ();
+  if (!str)
+    return;
+  char dst[6];
+  strcpy (dst, str);
+}
+
+void test11 (void)
+{
+  char *str = return_hello ();
+  if (!str)
+    return;
+    char dst[5];
+    strncpy (dst, str, 6); /* { dg-line test11 } */
+
+  /* { dg-warning "overflow" "warning" { target *-*-* } test11 } */
+  /* { dg-message "dst" "note" { target *-*-* } test11 } */
+}
+
+void test12 (void)
+{
+  char *str = return_hello ();
+  if (!str)
+    return;
+  char dst[6];
+  strcpy (dst, str);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/strcpy-3.c b/gcc/testsuite/gcc.dg/analyzer/strcpy-3.c
new file mode 100644
index 00000000000..a38f9a7641f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/strcpy-3.c
@@ -0,0 +1,23 @@
+#include <string.h>
+#include "analyzer-decls.h"
+
+void test_1 (void)
+{
+  char str[] = "Hello";
+  char buf[6];
+  char *result = strcpy (buf, str);
+  __analyzer_describe (1, result); /* { dg-warning "region_svalue.*?'buf'" } */
+  __analyzer_eval (result == buf); /* { dg-warning "TRUE" } */
+  __analyzer_eval (buf[0] == 'H'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (buf[1] == 'e'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (buf[2] == 'l'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (buf[3] == 'l'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (buf[4] == 'o'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (buf[5] == 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (result[0] == 'H'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (result[1] == 'e'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (result[2] == 'l'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (result[3] == 'l'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (result[4] == 'o'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (result[5] == 0); /* { dg-warning "TRUE" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/strncpy-1.c b/gcc/testsuite/gcc.dg/analyzer/strncpy-1.c
new file mode 100644
index 00000000000..ea051eb761a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/strncpy-1.c
@@ -0,0 +1,23 @@
+#include <string.h>
+#include "analyzer-decls.h"
+
+void test_1 (void)
+{
+  char str[] = "Hello";
+  char buf[6];
+  char *result = strncpy (buf, str, 6);
+  __analyzer_describe (1, result); /* { dg-warning "region_svalue.*?'buf'" } */
+  __analyzer_eval (result == buf); /* { dg-warning "TRUE" } */
+  __analyzer_eval (buf[0] == 'H'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (buf[1] == 'e'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (buf[2] == 'l'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (buf[3] == 'l'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (buf[4] == 'o'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (buf[5] == 0); /* { dg-warning "TRUE" } */
+  __analyzer_eval (result[0] == 'H'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (result[1] == 'e'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (result[2] == 'l'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (result[3] == 'l'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (result[4] == 'o'); /* { dg-warning "TRUE" } */
+  __analyzer_eval (result[5] == 0); /* { dg-warning "TRUE" } */
+}
