From 362ca98bbdfef14f7f85f53e6d12fdcd8120ea22 Mon Sep 17 00:00:00 2001 From: Ygor Souza Date: Fri, 12 Jul 2024 21:54:37 +0200 Subject: [PATCH 1/3] fix(postgres): don't panic if `M` or `C` Notice fields are not UTF-8 This has been observed with an old version of PostgreSQL (11.0.4) running on Windows Server 2016 with windows-1252 encoding and French locale. This change replaces invalid UTF-8 fields with a default string, so the other fields can still be read if they are valid. --- sqlx-postgres/src/message/response.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/sqlx-postgres/src/message/response.rs b/sqlx-postgres/src/message/response.rs index bb79ddd46d..9ee4ea09bf 100644 --- a/sqlx-postgres/src/message/response.rs +++ b/sqlx-postgres/src/message/response.rs @@ -6,6 +6,11 @@ use sqlx_core::bytes::Bytes; use crate::error::Error; use crate::io::Decode; +const INVALID_UTF8: &str = "Postgres returned a non-UTF-8 string for its error message. \ + This is most likely due to an error that occurred during authentication and \ + the default lc_messages locale is not binary-compatible with UTF-8. \ + See the server logs for the error details."; + #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u8)] pub enum PgSeverity { @@ -100,8 +105,7 @@ impl Notice { #[inline] fn get_cached_str(&self, cache: (u16, u16)) -> &str { - // unwrap: this cannot fail at this stage - from_utf8(&self.storage[cache.0 as usize..cache.1 as usize]).unwrap() + from_utf8(&self.storage[cache.0 as usize..cache.1 as usize]).unwrap_or(INVALID_UTF8) } } @@ -203,13 +207,7 @@ impl<'a> Iterator for Fields<'a> { fn notice_protocol_err() -> Error { // https://github.com/launchbadge/sqlx/issues/1144 - Error::Protocol( - "Postgres returned a non-UTF-8 string for its error message. \ - This is most likely due to an error that occurred during authentication and \ - the default lc_messages locale is not binary-compatible with UTF-8. \ - See the server logs for the error details." - .into(), - ) + Error::Protocol(INVALID_UTF8.into()) } #[test] From 6e915f2bca9ea553934bc31813cf59b360aa0c25 Mon Sep 17 00:00:00 2001 From: Ygor Souza Date: Tue, 16 Jul 2024 06:05:06 +0200 Subject: [PATCH 2/3] Revert "fix(postgres): don't panic if `M` or `C` Notice fields are not UTF-8" This reverts commit 362ca98bbdfef14f7f85f53e6d12fdcd8120ea22. --- sqlx-postgres/src/message/response.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/sqlx-postgres/src/message/response.rs b/sqlx-postgres/src/message/response.rs index 9ee4ea09bf..bb79ddd46d 100644 --- a/sqlx-postgres/src/message/response.rs +++ b/sqlx-postgres/src/message/response.rs @@ -6,11 +6,6 @@ use sqlx_core::bytes::Bytes; use crate::error::Error; use crate::io::Decode; -const INVALID_UTF8: &str = "Postgres returned a non-UTF-8 string for its error message. \ - This is most likely due to an error that occurred during authentication and \ - the default lc_messages locale is not binary-compatible with UTF-8. \ - See the server logs for the error details."; - #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u8)] pub enum PgSeverity { @@ -105,7 +100,8 @@ impl Notice { #[inline] fn get_cached_str(&self, cache: (u16, u16)) -> &str { - from_utf8(&self.storage[cache.0 as usize..cache.1 as usize]).unwrap_or(INVALID_UTF8) + // unwrap: this cannot fail at this stage + from_utf8(&self.storage[cache.0 as usize..cache.1 as usize]).unwrap() } } @@ -207,7 +203,13 @@ impl<'a> Iterator for Fields<'a> { fn notice_protocol_err() -> Error { // https://github.com/launchbadge/sqlx/issues/1144 - Error::Protocol(INVALID_UTF8.into()) + Error::Protocol( + "Postgres returned a non-UTF-8 string for its error message. \ + This is most likely due to an error that occurred during authentication and \ + the default lc_messages locale is not binary-compatible with UTF-8. \ + See the server logs for the error details." + .into(), + ) } #[test] From 7aa6ca1a2bbdaaf62f436ac9c523e244081ee702 Mon Sep 17 00:00:00 2001 From: Ygor Souza Date: Tue, 16 Jul 2024 06:08:48 +0200 Subject: [PATCH 3/3] Check that Notice M and C fields are valid UTF-8 Otherwise, we return the invalid UTF-8 error to avoid panicking later. --- sqlx-postgres/src/message/response.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sqlx-postgres/src/message/response.rs b/sqlx-postgres/src/message/response.rs index bb79ddd46d..ec3c880886 100644 --- a/sqlx-postgres/src/message/response.rs +++ b/sqlx-postgres/src/message/response.rs @@ -153,13 +153,19 @@ impl Decode<'_> for Notice { } b'M' => { + _ = from_utf8(&buf[v.0 as usize..v.1 as usize]) + .map_err(|_| notice_protocol_err())?; message = v; } b'C' => { + _ = from_utf8(&buf[v.0 as usize..v.1 as usize]) + .map_err(|_| notice_protocol_err())?; code = v; } + // If more fields are added, make sure to check that they are valid UTF-8, + // otherwise the get_cached_str method will panic. _ => {} } }