@@ -2701,7 +2701,7 @@ CreateSharedBackendStatus(void)
27012701 buffer = BackendActivityBuffer ;
27022702 for (i = 0 ; i < NumBackendStatSlots ; i ++ )
27032703 {
2704- BackendStatusArray [i ].st_activity = buffer ;
2704+ BackendStatusArray [i ].st_activity_raw = buffer ;
27052705 buffer += pgstat_track_activity_query_size ;
27062706 }
27072707 }
@@ -2922,11 +2922,11 @@ pgstat_bestart(void)
29222922#endif
29232923 beentry -> st_state = STATE_UNDEFINED ;
29242924 beentry -> st_appname [0 ] = '\0' ;
2925- beentry -> st_activity [0 ] = '\0' ;
2925+ beentry -> st_activity_raw [0 ] = '\0' ;
29262926 /* Also make sure the last byte in each string area is always 0 */
29272927 beentry -> st_clienthostname [NAMEDATALEN - 1 ] = '\0' ;
29282928 beentry -> st_appname [NAMEDATALEN - 1 ] = '\0' ;
2929- beentry -> st_activity [pgstat_track_activity_query_size - 1 ] = '\0' ;
2929+ beentry -> st_activity_raw [pgstat_track_activity_query_size - 1 ] = '\0' ;
29302930 beentry -> st_progress_command = PROGRESS_COMMAND_INVALID ;
29312931 beentry -> st_progress_command_target = InvalidOid ;
29322932
@@ -3017,7 +3017,7 @@ pgstat_report_activity(BackendState state, const char *cmd_str)
30173017 pgstat_increment_changecount_before (beentry );
30183018 beentry -> st_state = STATE_DISABLED ;
30193019 beentry -> st_state_start_timestamp = 0 ;
3020- beentry -> st_activity [0 ] = '\0' ;
3020+ beentry -> st_activity_raw [0 ] = '\0' ;
30213021 beentry -> st_activity_start_timestamp = 0 ;
30223022 /* st_xact_start_timestamp and wait_event_info are also disabled */
30233023 beentry -> st_xact_start_timestamp = 0 ;
@@ -3034,8 +3034,12 @@ pgstat_report_activity(BackendState state, const char *cmd_str)
30343034 start_timestamp = GetCurrentStatementStartTimestamp ();
30353035 if (cmd_str != NULL )
30363036 {
3037- len = pg_mbcliplen (cmd_str , strlen (cmd_str ),
3038- pgstat_track_activity_query_size - 1 );
3037+ /*
3038+ * Compute length of to-be-stored string unaware of multi-byte
3039+ * characters. For speed reasons that'll get corrected on read, rather
3040+ * than computed every write.
3041+ */
3042+ len = Min (strlen (cmd_str ), pgstat_track_activity_query_size - 1 );
30393043 }
30403044 current_timestamp = GetCurrentTimestamp ();
30413045
@@ -3049,8 +3053,8 @@ pgstat_report_activity(BackendState state, const char *cmd_str)
30493053
30503054 if (cmd_str != NULL )
30513055 {
3052- memcpy ((char * ) beentry -> st_activity , cmd_str , len );
3053- beentry -> st_activity [len ] = '\0' ;
3056+ memcpy ((char * ) beentry -> st_activity_raw , cmd_str , len );
3057+ beentry -> st_activity_raw [len ] = '\0' ;
30543058 beentry -> st_activity_start_timestamp = start_timestamp ;
30553059 }
30563060
@@ -3278,8 +3282,8 @@ pgstat_read_current_status(void)
32783282 */
32793283 strcpy (localappname , (char * ) beentry -> st_appname );
32803284 localentry -> backendStatus .st_appname = localappname ;
3281- strcpy (localactivity , (char * ) beentry -> st_activity );
3282- localentry -> backendStatus .st_activity = localactivity ;
3285+ strcpy (localactivity , (char * ) beentry -> st_activity_raw );
3286+ localentry -> backendStatus .st_activity_raw = localactivity ;
32833287 localentry -> backendStatus .st_ssl = beentry -> st_ssl ;
32843288#ifdef USE_SSL
32853289 if (beentry -> st_ssl )
@@ -3945,10 +3949,13 @@ pgstat_get_backend_current_activity(int pid, bool checkUser)
39453949 /* Now it is safe to use the non-volatile pointer */
39463950 if (checkUser && !superuser () && beentry -> st_userid != GetUserId ())
39473951 return "<insufficient privilege>" ;
3948- else if (* (beentry -> st_activity ) == '\0' )
3952+ else if (* (beentry -> st_activity_raw ) == '\0' )
39493953 return "<command string not enabled>" ;
39503954 else
3951- return beentry -> st_activity ;
3955+ {
3956+ /* this'll leak a bit of memory, but that seems acceptable */
3957+ return pgstat_clip_activity (beentry -> st_activity_raw );
3958+ }
39523959 }
39533960
39543961 beentry ++ ;
@@ -3994,7 +4001,7 @@ pgstat_get_crashed_backend_activity(int pid, char *buffer, int buflen)
39944001 if (beentry -> st_procpid == pid )
39954002 {
39964003 /* Read pointer just once, so it can't change after validation */
3997- const char * activity = beentry -> st_activity ;
4004+ const char * activity = beentry -> st_activity_raw ;
39984005 const char * activity_last ;
39994006
40004007 /*
@@ -4017,7 +4024,8 @@ pgstat_get_crashed_backend_activity(int pid, char *buffer, int buflen)
40174024 /*
40184025 * Copy only ASCII-safe characters so we don't run into encoding
40194026 * problems when reporting the message; and be sure not to run off
4020- * the end of memory.
4027+ * the end of memory. As only ASCII characters are reported, it
4028+ * doesn't seem necessary to perform multibyte aware clipping.
40214029 */
40224030 ascii_safe_strlcpy (buffer , activity ,
40234031 Min (buflen , pgstat_track_activity_query_size ));
@@ -6270,3 +6278,30 @@ pgstat_db_requested(Oid databaseid)
62706278
62716279 return false;
62726280}
6281+
6282+ /*
6283+ * Convert a potentially unsafely truncated activity string (see
6284+ * PgBackendStatus.st_activity_raw's documentation) into a correctly truncated
6285+ * one.
6286+ *
6287+ * The returned string is allocated in the caller's memory context and may be
6288+ * freed.
6289+ */
6290+ char *
6291+ pgstat_clip_activity (const char * activity )
6292+ {
6293+ int rawlen = strnlen (activity , pgstat_track_activity_query_size - 1 );
6294+ int cliplen ;
6295+
6296+ /*
6297+ * All supported server-encodings make it possible to determine the length
6298+ * of a multi-byte character from its first byte (this is not the case for
6299+ * client encodings, see GB18030). As st_activity is always stored using
6300+ * server encoding, this allows us to perform multi-byte aware truncation,
6301+ * even if the string earlier was truncated in the middle of a multi-byte
6302+ * character.
6303+ */
6304+ cliplen = pg_mbcliplen (activity , rawlen ,
6305+ pgstat_track_activity_query_size - 1 );
6306+ return pnstrdup (activity , cliplen );
6307+ }
0 commit comments