diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index e2e42fe4a2de2ca7d9d18e5eac8f1b707649f1a7..2b907ddf80499731d4bfdf5d7c94f5f0fb255bba 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -701,7 +701,8 @@ int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd)
 my_bool	STDCALL mysql_change_user(MYSQL *mysql, const char *user,
 				  const char *passwd, const char *db)
 {
-  char buff[512],*end=buff;
+  char buff[USERNAME_LENGTH+SCRAMBLED_PASSWORD_CHAR_LENGTH+NAME_LEN+2];
+  char *end= buff;
   int rc;
   CHARSET_INFO *saved_cs= mysql->charset;
 
@@ -723,7 +724,7 @@ my_bool	STDCALL mysql_change_user(MYSQL *mysql, const char *user,
     passwd="";
 
   /* Store user into the buffer */
-  end=strmov(end,user)+1;
+  end= strmake(end, user, USERNAME_LENGTH) + 1;
 
   /* write scrambled password according to server capabilities */
   if (passwd[0])
@@ -743,7 +744,7 @@ my_bool	STDCALL mysql_change_user(MYSQL *mysql, const char *user,
   else
     *end++= '\0';                               /* empty password */
   /* Add database if needed */
-  end= strmov(end, db ? db : "") + 1;
+  end= strmake(end, db ? db : "", NAME_LEN) + 1;
 
   /* Add character set number. */
 
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
index d3ba0660198b785a4fa901398c3ba057253684cd..808d6fe30dec00940481fd001b94f41a581cd0af 100644
--- a/tests/mysql_client_test.c
+++ b/tests/mysql_client_test.c
@@ -17100,6 +17100,99 @@ static void test_bug31418()
 }
 
 
+
+/**
+  Bug#31669 Buffer overflow in mysql_change_user()
+*/
+
+#define LARGE_BUFFER_SIZE 2048
+
+static void test_bug31669()
+{
+  int rc;
+  static char buff[LARGE_BUFFER_SIZE+1];
+#ifndef EMBEDDED_LIBRARY
+  static char user[USERNAME_LENGTH+1];
+  static char db[NAME_LEN+1];
+  static char query[LARGE_BUFFER_SIZE*2];
+#endif
+
+  DBUG_ENTER("test_bug31669");
+  myheader("test_bug31669");
+
+  rc= mysql_change_user(mysql, NULL, NULL, NULL);
+  DIE_UNLESS(rc);
+
+  rc= mysql_change_user(mysql, "", "", "");
+  DIE_UNLESS(rc);
+
+  memset(buff, 'a', sizeof(buff));
+
+  rc= mysql_change_user(mysql, buff, buff, buff);
+  DIE_UNLESS(rc);
+
+  rc = mysql_change_user(mysql, opt_user, opt_password, current_db);
+  DIE_UNLESS(!rc);
+
+#ifndef EMBEDDED_LIBRARY
+  memset(db, 'a', sizeof(db));
+  db[NAME_LEN]= 0;
+  strxmov(query, "CREATE DATABASE IF NOT EXISTS ", db, NullS);
+  rc= mysql_query(mysql, query);
+  myquery(rc);
+
+  memset(user, 'b', sizeof(user));
+  user[USERNAME_LENGTH]= 0;
+  memset(buff, 'c', sizeof(buff));
+  buff[LARGE_BUFFER_SIZE]= 0;
+  strxmov(query, "GRANT ALL PRIVILEGES ON *.* TO '", user, "'@'%' IDENTIFIED BY "
+                 "'", buff, "' WITH GRANT OPTION", NullS);
+  rc= mysql_query(mysql, query);
+  myquery(rc);
+
+  rc= mysql_query(mysql, "FLUSH PRIVILEGES");
+  myquery(rc);
+
+  rc= mysql_change_user(mysql, user, buff, db);
+  DIE_UNLESS(!rc);
+
+  user[USERNAME_LENGTH-1]= 'a';
+  rc= mysql_change_user(mysql, user, buff, db);
+  DIE_UNLESS(rc);
+
+  user[USERNAME_LENGTH-1]= 'b';
+  buff[LARGE_BUFFER_SIZE-1]= 'd';
+  rc= mysql_change_user(mysql, user, buff, db);
+  DIE_UNLESS(rc);
+
+  buff[LARGE_BUFFER_SIZE-1]= 'c';
+  db[NAME_LEN-1]= 'e';
+  rc= mysql_change_user(mysql, user, buff, db);
+  DIE_UNLESS(rc);
+
+  db[NAME_LEN-1]= 'a';
+  rc= mysql_change_user(mysql, user, buff, db);
+  DIE_UNLESS(!rc);
+
+  rc= mysql_change_user(mysql, user + 1, buff + 1, db + 1);
+  DIE_UNLESS(rc);
+
+  rc = mysql_change_user(mysql, opt_user, opt_password, current_db);
+  DIE_UNLESS(!rc);
+
+  strxmov(query, "DROP DATABASE ", db, NullS);
+  rc= mysql_query(mysql, query);
+  myquery(rc);
+
+  strxmov(query, "DELETE FROM mysql.user WHERE User='", user, "'", NullS);
+  rc= mysql_query(mysql, query);
+  myquery(rc);
+  DIE_UNLESS(mysql_affected_rows(mysql) == 1);
+#endif
+
+  DBUG_VOID_RETURN;
+}
+
 /*
   Read and parse arguments and MySQL options from my.cnf
 */
@@ -17403,6 +17496,7 @@ static struct my_tests_st my_tests[]= {
   { "test_bug30472", test_bug30472 },
   { "test_bug20023", test_bug20023 },
   { "test_bug31418", test_bug31418 },
+  { "test_bug31669", test_bug31669 },
   { 0, 0 }
 };