We always truncated the name at 20 bytes instead of characters. In
case 20 bytes were in the middle of a multi-byte character, then the
string became invalid and various parts of the code would error
out (e.g. XML parsing of that string). Let's instead properly
truncate it after 20 characters instead.
We cannot test this in our test suite because we would need to know
what locales are installed on the system where the tests are ran and
if there is supported one (most probably there will be, but we cannot
be 100% sure), we could initialize gettext in qemuxml2argvtest, but
there would still be a chance of getting two different (both valid,
though) results.
In order to test this it is enough to start a machine with a name for
which trimming it after 20 bytes would create invalid sequence (e.g.
1234567890123456789č where č is any multi-byte character). Then start
the domain and restart libvirtd. The domain would disappear because
such illegal sequence will not go through the XML parser. And that's
not a bug of the parser, it should not be in the XML in the first
place, but since we don't use any sophisticated formatter, just
mash some strings together, the formatting succeeds.
Resolves:
https://bugzilla.redhat.com/show_bug.cgi?id=1448766
Signed-off-by: Martin Kletzander <mkletzan(a)redhat.com>
---
src/conf/domain_conf.c | 45 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 42 insertions(+), 3 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 47eba4dbb315..dd73158f028b 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -27131,6 +27131,8 @@ virDomainDefHasMemballoon(const virDomainDef *def)
}
+#define VIR_DOMAIN_SHORT_NAME_MAX 20
+
/**
* virDomainObjGetShortName:
* @vm: Machine for which to get a name
@@ -27141,15 +27143,52 @@ virDomainDefHasMemballoon(const virDomainDef *def)
char *
virDomainObjGetShortName(const virDomainDef *def)
{
- const int dommaxlen = 20;
+ wchar_t wshortname[VIR_DOMAIN_SHORT_NAME_MAX + 1] = {0};
+ size_t len = 0;
+ char *shortname = NULL;
char *ret = NULL;
- ignore_value(virAsprintf(&ret, "%d-%.*s",
- def->id, dommaxlen, def->name));
+ /* No need to do the whole conversion thing when there are no multibyte
+ * characters. The same applies for illegal sequences as they can occur
+ * with incompatible locales. */
+ len = mbstowcs(NULL, def->name, 0);
+ if ((len == (size_t) -1 && errno == EILSEQ) ||
+ len == strlen(def->name)) {
+ ignore_value(virAsprintf(&ret, "%d-%.*s", def->id,
+ VIR_DOMAIN_SHORT_NAME_MAX, def->name));
+ return ret;
+ }
+
+ if (len == (size_t) -1 ||
+ mbstowcs(wshortname, def->name, VIR_DOMAIN_SHORT_NAME_MAX) == (size_t) -1) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("Cannot convert domain name to wide character
string"));
+ return NULL;
+ }
+
+ len = wcstombs(NULL, wshortname, 0);
+ if (len == (size_t) -1)
+ return NULL;
+ if (len > VIR_DOMAIN_SHORT_NAME_MAX)
+ len = VIR_DOMAIN_SHORT_NAME_MAX;
+
+ if (VIR_ALLOC_N(shortname, len + 1) < 0)
+ return NULL;
+
+ if (wcstombs(shortname, wshortname, len) == (size_t) -1) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("Cannot convert domain name from wide character
string"));
+ goto cleanup;
+ }
+
+ ignore_value(virAsprintf(&ret, "%d-%s", def->id, shortname));
+ cleanup:
+ VIR_FREE(shortname);
return ret;
}
+#undef VIR_DOMAIN_SHORT_NAME_MAX
int
virDomainGetBlkioParametersAssignFromDef(virDomainDefPtr def,
--
2.14.1