Print this page
OS-1723 DTrace should speak JSON (review fixes)


 862                         strsize = state->dts_options[DTRACEOPT_STRSIZE];
 863                 } else {
 864                         /*
 865                          * In helper context, we have a NULL state; fall back
 866                          * to using the system-wide default for the string size
 867                          * in this case.
 868                          */
 869                         strsize = dtrace_strsize_default;
 870                 }
 871 
 872                 sz = dtrace_strlen(src, strsize) + 1;
 873         } else {
 874                 sz = type->dtdt_size;
 875         }
 876 
 877         return (dtrace_canload((uintptr_t)src, sz, mstate, vstate));
 878 }
 879 
 880 /*
 881  * Convert a string to a signed integer using safe loads.




 882  */
 883 static int64_t
 884 dtrace_strtoll(char *input, int base, size_t limit)
 885 {
 886         uintptr_t pos = (uintptr_t)input;
 887         int64_t val = 0;
 888         int x;
 889         boolean_t neg = B_FALSE;
 890         char c, cc, ccc;
 891         uintptr_t end = pos + limit;
 892 
 893         /* eat whitespace */


 894         while ((c = dtrace_load8(pos)) == ' ' || c == '\t')
 895                 pos++;
 896 
 897         /* sign? */


 898         if (c == '-' || c == '+') {
 899                 if (c == '-')
 900                         neg = B_TRUE;
 901                 c = dtrace_load8(++pos);
 902         }
 903 
 904         /* hex prefix? */



 905         if (base == 16 && c == '0' && ((cc = dtrace_load8(pos + 1)) == 'x' ||
 906             cc == 'X') && isxdigit(ccc = dtrace_load8(pos + 2))) {
 907                 pos += 2; /* skip over leading "0x" or "0X" */
 908                 c = ccc;
 909         }
 910 
 911         /* read in digits */


 912         for (; pos < end && c != '\0' && lisalnum(c) && (x = DIGIT(c)) < base;
 913             c = dtrace_load8(++pos))
 914                 val = val * base + x;
 915 
 916         return (neg ? -val : val);
 917 }
 918 
 919 /*
 920  * Compare two strings using safe loads.
 921  */
 922 static int
 923 dtrace_strncmp(char *s1, char *s2, size_t limit)
 924 {
 925         uint8_t c1, c2;
 926         volatile uint16_t *flags;
 927 
 928         if (s1 == s2 || limit == 0)
 929                 return (0);
 930 
 931         flags = (volatile uint16_t *)&cpu_core[CPU->cpu_id].cpuc_dtrace_flags;


3377                         return (0);
3378 
3379                 /*
3380                  * It is always safe to dereference one's own t_lwp pointer in
3381                  * the event that this pointer is non-NULL.  (This is true
3382                  * because threads and lwps don't clean up their own state --
3383                  * they leave that task to whomever reaps them.)
3384                  */
3385                 if ((lwp = curthread->t_lwp) == NULL)
3386                         return (0);
3387 
3388                 return ((uint64_t)lwp->lwp_errno);
3389         }
3390         default:
3391                 DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
3392                 return (0);
3393         }
3394 }
3395 
3396 
3397 typedef enum json_state {
3398         JSON_REST = 1,
3399         JSON_OBJECT,
3400         JSON_STRING,
3401         JSON_STRING_ESCAPE,
3402         JSON_STRING_ESCAPE_UNICODE,
3403         JSON_COLON,
3404         JSON_COMMA,
3405         JSON_VALUE,
3406         JSON_IDENTIFIER,
3407         JSON_NUMBER,
3408         JSON_NUMBER_FRAC,
3409         JSON_NUMBER_EXP,
3410         JSON_COLLECT_OBJECT
3411 } json_state_t;
3412 
3413 /*
3414  * This function possesses just enough knowledge about JSON to extract a single
3415  * value from a JSON string and store it in the scratch buffer.  It is able
3416  * to extract nested object values, and members of arrays by index.
3417  *
3418  * elemlist is a list of JSON keys, stored as packed NUL-terminated strings, to
3419  * be looked up as we descend into the object tree.  e.g.
3420  *
3421  *    foo[0].bar.baz[32] --> "foo" NUL "0" NUL "bar" NUL "baz" NUL "32" NUL
3422  *       with nelems = 5.


































































3423  */
3424 static char *
3425 dtrace_json(uint64_t size, uintptr_t json, char *elemlist, int nelems,
3426     char *dest)
3427 {
3428         json_state_t state = JSON_REST;
3429         uint64_t i;
3430         int64_t array_elem = INT64_MIN;
3431         int64_t array_pos = 0;
3432         uint8_t escape_unicount = 0;
3433         boolean_t string_is_key = B_FALSE;
3434         boolean_t collect_object = B_FALSE;
3435         boolean_t found_key = B_FALSE;
3436         boolean_t in_array = B_FALSE;
3437         uint8_t braces = 0, brackets = 0;
3438         char *elem = elemlist;
3439         char *dd = dest;
3440         uintptr_t cur;
3441 
3442         for (cur = json; cur < json + size; cur++) {
3443                 char cc = dtrace_load8(cur);
3444                 if (cc == '\0' || braces > 250)
3445                         return (NULL);
3446 
3447                 switch (state) {
3448                 case JSON_REST:
3449                         if (cc == ' ' || cc == '\t' || cc == '\n' || cc == '\r')
3450                                 break; /* eat whitespace */
3451 
3452                         if (cc == '{') {
3453                                 state = JSON_OBJECT;
3454                                 break;
3455                         }
3456 
3457                         if (cc == '[') {
3458                                 in_array = B_TRUE;
3459                                 array_pos = 0;
3460                                 array_elem = dtrace_strtoll(elem, 10, size);
3461                                 found_key = !!(array_elem == 0);
3462                                 state = JSON_VALUE;
3463                                 break;
3464                         }
3465 
3466                         /* ERROR: expected object or array */


3467                         return (NULL);
3468                 case JSON_OBJECT:
3469                         if (cc == ' ' || cc == '\t' || cc == '\n' || cc == '\r')
3470                                 break; /* eat whitespace */
3471 
3472                         if (cc == '"') {
3473                                 state = JSON_STRING;
3474                                 string_is_key = B_TRUE;
3475                                 break;
3476                         }
3477 
3478                         /* ERROR: key not found! */




3479                         return (NULL);
3480                 case JSON_STRING:
3481                         if (cc == '\\') {
3482                                 *dd++ = '\\';
3483                                 state = JSON_STRING_ESCAPE;
3484                                 break;
3485                         }
3486 
3487                         if (cc == '"') {
3488                                 if (collect_object) {
3489                                         /*
3490                                          * We don't reset the dest here, as
3491                                          * the string is part of a larger
3492                                          * object being collected.
3493                                          */
3494                                         *dd++ = cc;
3495                                         collect_object = B_FALSE;
3496                                         state = JSON_COLLECT_OBJECT;
3497                                         break;
3498                                 }
3499                                 *dd = '\0';
3500                                 dd = dest; /* reset string buffer */
3501                                 if (string_is_key) {
3502                                         if (dtrace_strncmp(dest, elem,
3503                                             size) == 0)
3504                                                 found_key = B_TRUE;
3505                                 } else if (found_key) {
3506                                         if (nelems > 1) {
3507                                                 /*
3508                                                  * We expected an object, not
3509                                                  * this string.
3510                                                  */
3511                                                 return (NULL);
3512                                         }
3513                                         return (dest);
3514                                 }
3515                                 state = string_is_key ? JSON_COLON :
3516                                     JSON_COMMA;
3517                                 string_is_key = B_FALSE;
3518                                 break;
3519                         }
3520 
3521                         *dd++ = cc;
3522                         break;
3523                 case JSON_STRING_ESCAPE:
3524                         *dd++ = cc;
3525                         if (cc == 'u') {
3526                                 escape_unicount = 0;
3527                                 state = JSON_STRING_ESCAPE_UNICODE;
3528                         } else {
3529                                 state = JSON_STRING;
3530                         }
3531                         break;
3532                 case JSON_STRING_ESCAPE_UNICODE:
3533                         if (!isxdigit(cc))
3534                                 /* ERROR: unvalid unicode escape */



3535                                 return (NULL);

3536 
3537                         *dd++ = cc;
3538                         if (++escape_unicount == 4)
3539                                 state = JSON_STRING;
3540                         break;
3541                 case JSON_COLON:
3542                         if (cc == ' ' || cc == '\t' || cc == '\n' || cc == '\r')
3543                                 break; /* eat whitespace */
3544 
3545                         if (cc == ':') {
3546                                 state = JSON_VALUE;
3547                                 break;
3548                         }
3549 
3550                         /* ERROR: expected colon */


3551                         return (NULL);
3552                 case JSON_COMMA:
3553                         if (cc == ' ' || cc == '\t' || cc == '\n' || cc == '\r')
3554                                 break; /* eat whitespace */
3555 
3556                         if (cc == ',') {
3557                                 if (in_array) {
3558                                         state = JSON_VALUE;
3559                                         if (++array_pos == array_elem)
3560                                                 found_key = B_TRUE;
3561                                 } else {
3562                                         state = JSON_OBJECT;
3563                                 }
3564                                 break;
3565                         }
3566 
3567                         /* ERROR: key not found or expected comma */




3568                         return (NULL);
3569                 case JSON_IDENTIFIER:
3570                         if (cc >= 'a' && cc <= 'z') {
3571                                 *dd++ = cc;
3572                                 break;
3573                         }
3574 
3575                         *dd = '\0';
3576                         dd = dest; /* reset string buffer */
3577 
3578                         if (dtrace_strncmp(dest, "true", 5) == 0 ||
3579                             dtrace_strncmp(dest, "false", 6) == 0 ||
3580                             dtrace_strncmp(dest, "null", 5) == 0) {
3581                                 if (found_key) {
3582                                         if (nelems > 1) {
3583                                                 /*
3584                                                  * We expected an object, not
3585                                                  * this identifier.
3586                                                  */
3587                                                 return (NULL);
3588                                         }
3589                                         return (dest);
3590                                 } else {
3591                                         cur--;
3592                                         state = JSON_COMMA;
3593                                         break;
3594                                 }
3595                         }
3596 
3597                         /* ERROR: unexpected identifier */



3598                         return (NULL);
3599                 case JSON_NUMBER:
3600                         if (cc == '.') {
3601                                 *dd++ = cc;
3602                                 state = JSON_NUMBER_FRAC;
3603                                 break;
3604                         }
3605 
3606                         if (cc == 'x' || cc == 'X')
3607                                 /* ERROR: spec explicitly excludes hex */



3608                                 return (NULL);

3609 
3610                         /* FALLTHRU */
3611                 case JSON_NUMBER_FRAC:
3612                         if (cc == 'e' || cc == 'E') {
3613                                 *dd++ = cc;
3614                                 state = JSON_NUMBER_EXP;
3615                                 break;
3616                         }
3617 
3618                         if (cc == '+' || cc == '-') {
3619                                 /*
3620                                  * ERROR: expect sign as part of exponent only
3621                                  */
3622                                 return (NULL);
3623                         }
3624                         /* FALLTHRU */
3625                 case JSON_NUMBER_EXP:
3626                         if ((cc >= '0' && cc <= '9') || cc == '+' ||
3627                             cc == '-') {
3628                                 *dd++ = cc;
3629                                 break;
3630                         }
3631 
3632                         *dd = '\0';
3633                         dd = dest; /* reset string buffer */
3634                         if (found_key) {
3635                                 if (nelems > 1) {
3636                                         /*
3637                                          * We expected an object, not this
3638                                          * number.
3639                                          */
3640                                         return (NULL);
3641                                 }
3642                                 return (dest);
3643                         }
3644 
3645                         cur--;
3646                         state = JSON_COMMA;
3647                         break;
3648                 case JSON_VALUE:
3649                         if (cc == ' ' || cc == '\t' || cc == '\n' || cc == '\r')
3650                                 break; /* eat whitespace */
3651 
3652                         if (cc == '{' || cc == '[') {
3653                                 if (nelems > 1 && found_key) {
3654                                         in_array = !!(cc == '[');
3655                                         /*
3656                                          * If our element selector directs us
3657                                          * to descend into this nested object,
3658                                          * then move to the next selector
3659                                          * element in the list and restart the
3660                                          * state machine.
3661                                          */
3662                                         while (*elem != '\0')
3663                                                 elem++;
3664                                         elem++; /* skip the inter-element NUL */
3665                                         nelems--;
3666                                         dd = dest;
3667                                         if (in_array) {
3668                                                 state = JSON_VALUE;
3669                                                 array_pos = 0;
3670                                                 array_elem = dtrace_strtoll(
3671                                                     elem, 10, size);
3672                                                 found_key = !!(array_elem == 0);

3673                                         } else {
3674                                                 found_key = B_FALSE;
3675                                                 state = JSON_OBJECT;
3676                                         }
3677                                         break;
3678                                 }
3679 
3680                                 /*
3681                                  * Otherwise, we wish to either skip this
3682                                  * nested object or return it in full.
3683                                  */
3684                                 if (cc == '[')
3685                                         brackets = 1;
3686                                 else
3687                                         braces = 1;
3688                                 *dd++ = cc;
3689                                 state = JSON_COLLECT_OBJECT;
3690                                 break;
3691                         }
3692 
3693                         if (cc == '"') {
3694                                 state = JSON_STRING;
3695                                 break;
3696                         }
3697 
3698                         if (cc >= 'a' && cc <= 'z') {
3699                                 /* Here we deal with true, false and null */


3700                                 *dd++ = cc;
3701                                 state = JSON_IDENTIFIER;
3702                                 break;
3703                         }
3704 
3705                         if (cc == '-' || (cc >= '0' && cc <= '9')) {
3706                                 *dd++ = cc;
3707                                 state = JSON_NUMBER;
3708                                 break;
3709                         }
3710 
3711                         /* ERROR: unexpected character */


3712                         return (NULL);
3713                 case JSON_COLLECT_OBJECT:
3714                         if (cc == '\0')
3715                                 /* ERROR: unexpected end of input */


3716                                 return (NULL);
3717 
3718                         *dd++ = cc;
3719                         if (cc == '"') {
3720                                 collect_object = B_TRUE;
3721                                 state = JSON_STRING;
3722                                 break;
3723                         }
3724 
3725                         if (cc == ']') {
3726                                 if (brackets-- == 0) {
3727                                         /* ERROR: unbalanced brackets */


3728                                         return (NULL);
3729                                 }
3730                         } else if (cc == '}') {
3731                                 if (braces-- == 0) {
3732                                         /* ERROR: unbalanced braces */


3733                                         return (NULL);
3734                                 }
3735                         } else if (cc == '{') {
3736                                 braces++;
3737                         } else if (cc == '[') {
3738                                 brackets++;
3739                         }
3740 
3741                         if (brackets == 0 && braces == 0) {
3742                                 if (found_key) {
3743                                         *dd = '\0';
3744                                         return (dest);
3745                                 }
3746                                 dd = dest; /* reset string buffer */
3747                                 state = JSON_COMMA;
3748                         }
3749                         break;
3750                 }
3751         }
3752         return (NULL);
3753 }
3754 
3755 /*
3756  * Emulate the execution of DTrace ID subroutines invoked by the call opcode.
3757  * Notice that we don't bother validating the proper number of arguments or
3758  * their types in the tuple stack.  This isn't needed because all argument
3759  * interpretation is safe because of our load safety -- the worst that can
3760  * happen is that a bogus program can obtain bogus results.
3761  */
3762 static void
3763 dtrace_dif_subr(uint_t subr, uint_t rd, uint64_t *regs,
3764     dtrace_key_t *tupregs, int nargs,
3765     dtrace_mstate_t *mstate, dtrace_state_t *state)
3766 {
3767         volatile uint16_t *flags = &cpu_core[CPU->cpu_id].cpuc_dtrace_flags;


4467 
4468                 if (!dtrace_canload(json, jsonlen + 1, mstate, vstate) ||
4469                     !dtrace_canload(elem, elemlen + 1, mstate, vstate)) {
4470                         regs[rd] = NULL;
4471                         break;
4472                 }
4473 
4474                 if (!DTRACE_INSCRATCH(mstate, jsonlen + 1 + elemlen + 1)) {
4475                         DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
4476                         regs[rd] = NULL;
4477                         break;
4478                 }
4479 
4480                 /*
4481                  * Read the element selector and split it up into a packed list
4482                  * of strings.
4483                  */
4484                 for (cur = elem; cur < elem + elemlen; cur++) {
4485                         char cc = dtrace_load8(cur);
4486 
4487                         if (cur == elem && cc == '[')
4488                                 /* first element selector may be an array */




4489                                 continue;

4490 
4491                         if (cc == ']')
4492                                 continue;
4493 
4494                         if (cc == '.' || cc == '[') {
4495                                 nelems++;
4496                                 cc = '\0';
4497                         }
4498 
4499                         *ee++ = cc;
4500                 }
4501                 *ee++ = '\0';
4502 
4503                 if ((regs[rd] = (uintptr_t)dtrace_json(size, json, elemlist,
4504                     nelems, dest)) != NULL)
4505                         mstate->dtms_scratch_ptr += jsonlen + 1;
4506                 break;
4507         }
4508 
4509         case DIF_SUBR_TOUPPER:




 862                         strsize = state->dts_options[DTRACEOPT_STRSIZE];
 863                 } else {
 864                         /*
 865                          * In helper context, we have a NULL state; fall back
 866                          * to using the system-wide default for the string size
 867                          * in this case.
 868                          */
 869                         strsize = dtrace_strsize_default;
 870                 }
 871 
 872                 sz = dtrace_strlen(src, strsize) + 1;
 873         } else {
 874                 sz = type->dtdt_size;
 875         }
 876 
 877         return (dtrace_canload((uintptr_t)src, sz, mstate, vstate));
 878 }
 879 
 880 /*
 881  * Convert a string to a signed integer using safe loads.
 882  *
 883  * NOTE: This function uses various macros from strtolctype.h to manipulate
 884  * digit values, etc -- these have all been checked to ensure they make
 885  * no additional function calls.
 886  */
 887 static int64_t
 888 dtrace_strtoll(char *input, int base, size_t limit)
 889 {
 890         uintptr_t pos = (uintptr_t)input;
 891         int64_t val = 0;
 892         int x;
 893         boolean_t neg = B_FALSE;
 894         char c, cc, ccc;
 895         uintptr_t end = pos + limit;
 896 
 897         /*
 898          * Consume any whitespace preceding digits.
 899          */
 900         while ((c = dtrace_load8(pos)) == ' ' || c == '\t')
 901                 pos++;
 902 
 903         /*
 904          * Handle an explicit sign if one is present.
 905          */
 906         if (c == '-' || c == '+') {
 907                 if (c == '-')
 908                         neg = B_TRUE;
 909                 c = dtrace_load8(++pos);
 910         }
 911 
 912         /*
 913          * Check for an explicit hexadecimal prefix ("0x" or "0X") and skip it
 914          * if present.
 915          */
 916         if (base == 16 && c == '0' && ((cc = dtrace_load8(pos + 1)) == 'x' ||
 917             cc == 'X') && isxdigit(ccc = dtrace_load8(pos + 2))) {
 918                 pos += 2;
 919                 c = ccc;
 920         }
 921 
 922         /*
 923          * Read in contiguous digits until the first non-digit character.
 924          */
 925         for (; pos < end && c != '\0' && lisalnum(c) && (x = DIGIT(c)) < base;
 926             c = dtrace_load8(++pos))
 927                 val = val * base + x;
 928 
 929         return (neg ? -val : val);
 930 }
 931 
 932 /*
 933  * Compare two strings using safe loads.
 934  */
 935 static int
 936 dtrace_strncmp(char *s1, char *s2, size_t limit)
 937 {
 938         uint8_t c1, c2;
 939         volatile uint16_t *flags;
 940 
 941         if (s1 == s2 || limit == 0)
 942                 return (0);
 943 
 944         flags = (volatile uint16_t *)&cpu_core[CPU->cpu_id].cpuc_dtrace_flags;


3390                         return (0);
3391 
3392                 /*
3393                  * It is always safe to dereference one's own t_lwp pointer in
3394                  * the event that this pointer is non-NULL.  (This is true
3395                  * because threads and lwps don't clean up their own state --
3396                  * they leave that task to whomever reaps them.)
3397                  */
3398                 if ((lwp = curthread->t_lwp) == NULL)
3399                         return (0);
3400 
3401                 return ((uint64_t)lwp->lwp_errno);
3402         }
3403         default:
3404                 DTRACE_CPUFLAG_SET(CPU_DTRACE_ILLOP);
3405                 return (0);
3406         }
3407 }
3408 
3409 
3410 typedef enum dtrace_json_state {
3411         DTRACE_JSON_REST = 1,
3412         DTRACE_JSON_OBJECT,
3413         DTRACE_JSON_STRING,
3414         DTRACE_JSON_STRING_ESCAPE,
3415         DTRACE_JSON_STRING_ESCAPE_UNICODE,
3416         DTRACE_JSON_COLON,
3417         DTRACE_JSON_COMMA,
3418         DTRACE_JSON_VALUE,
3419         DTRACE_JSON_IDENTIFIER,
3420         DTRACE_JSON_NUMBER,
3421         DTRACE_JSON_NUMBER_FRAC,
3422         DTRACE_JSON_NUMBER_EXP,
3423         DTRACE_JSON_COLLECT_OBJECT
3424 } dtrace_json_state_t;
3425 
3426 /*
3427  * This function possesses just enough knowledge about JSON to extract a single
3428  * value from a JSON string and store it in the scratch buffer.  It is able
3429  * to extract nested object values, and members of arrays by index.
3430  *
3431  * elemlist is a list of JSON keys, stored as packed NUL-terminated strings, to
3432  * be looked up as we descend into the object tree.  e.g.
3433  *
3434  *    foo[0].bar.baz[32] --> "foo" NUL "0" NUL "bar" NUL "baz" NUL "32" NUL
3435  *       with nelems = 5.
3436  *
3437  * The run time of this function must be bounded above by strsize to limit the
3438  * amount of work done in probe context.  As such, it is implemented as a
3439  * simple state machine, reading one character at a time using safe loads
3440  * until we find the requested element, hit a parsing error or run off the
3441  * end of the object or string.
3442  *
3443  * As there is no way for a subroutine to return an error without interrupting
3444  * clause execution, we simply return NULL in the event of a missing key or any
3445  * other error condition.  Each NULL return in this function is commented with
3446  * the error condition it represents -- parsing or otherwise.
3447  *
3448  * The set of states for the state machine closely matches the JSON
3449  * specification (http://json.org/).  Briefly:
3450  *
3451  *   DTRACE_JSON_REST:
3452  *     Skip whitespace until we find either a top-level Object, moving
3453  *     to DTRACE_JSON_OBJECT; or an Array, moving to DTRACE_JSON_VALUE.
3454  *
3455  *   DTRACE_JSON_OBJECT:
3456  *     Locate the next key String in an Object.  Sets a flag to denote
3457  *     the next String as a key string and moves to DTRACE_JSON_STRING.
3458  *
3459  *   DTRACE_JSON_COLON:
3460  *     Skip whitespace until we find the colon that separates key Strings
3461  *     from their values.  Once found, move to DTRACE_JSON_VALUE.
3462  *
3463  *   DTRACE_JSON_VALUE:
3464  *     Detects the type of the next value (String, Number, Identifier, Object
3465  *     or Array) and routes to the states that process that type.  Here we also
3466  *     deal with the element selector list if we are requested to traverse down
3467  *     into the object tree.
3468  *
3469  *   DTRACE_JSON_COMMA:
3470  *     Skip whitespace until we find the comma that separates key-value pairs
3471  *     in Objects (returning to DTRACE_JSON_OBJECT) or values in Arrays
3472  *     (similarly DTRACE_JSON_VALUE).  All following literal value processing
3473  *     states return to this state at the end of their value, unless otherwise
3474  *     noted.
3475  *
3476  *   DTRACE_JSON_NUMBER, DTRACE_JSON_NUMBER_FRAC, DTRACE_JSON_NUMBER_EXP:
3477  *     Processes a Number literal from the JSON, including any exponent
3478  *     component that may be present.  Numbers are returned as strings, which
3479  *     may be passed to strtoll() if an integer is required.
3480  *
3481  *   DTRACE_JSON_IDENTIFIER:
3482  *     Processes a "true", "false" or "null" literal in the JSON.
3483  *
3484  *   DTRACE_JSON_STRING, DTRACE_JSON_STRING_ESCAPE,
3485  *   DTRACE_JSON_STRING_ESCAPE_UNICODE:
3486  *     Processes a String literal from the JSON, whether the String denotes
3487  *     a key, a value or part of a larger Object.  Handles all escape sequences
3488  *     present in the specification, including four-digit unicode characters,
3489  *     but merely includes the escape sequence without converting it to the
3490  *     actual escaped character.  If the String is flagged as a key, we
3491  *     move to DTRACE_JSON_COLON rather than DTRACE_JSON_COMMA.
3492  *
3493  *   DTRACE_JSON_COLLECT_OBJECT:
3494  *     This state collects an entire Object (or Array), correctly handling
3495  *     embedded strings.  If the full element selector list matches this nested
3496  *     object, we return the Object in full as a string.  If not, we use this
3497  *     state to skip to the next value at this level and continue processing.
3498  *
3499  * NOTE: This function uses various macros from strtolctype.h to manipulate
3500  * digit values, etc -- these have all been checked to ensure they make
3501  * no additional function calls.
3502  */
3503 static char *
3504 dtrace_json(uint64_t size, uintptr_t json, char *elemlist, int nelems,
3505     char *dest)
3506 {
3507         dtrace_json_state_t state = DTRACE_JSON_REST;
3508         uint64_t i;
3509         int64_t array_elem = INT64_MIN;
3510         int64_t array_pos = 0;
3511         uint8_t escape_unicount = 0;
3512         boolean_t string_is_key = B_FALSE;
3513         boolean_t collect_object = B_FALSE;
3514         boolean_t found_key = B_FALSE;
3515         boolean_t in_array = B_FALSE;
3516         uint32_t braces = 0, brackets = 0;
3517         char *elem = elemlist;
3518         char *dd = dest;
3519         uintptr_t cur;
3520 
3521         for (cur = json; cur < json + size; cur++) {
3522                 char cc = dtrace_load8(cur);
3523                 if (cc == '\0')
3524                         return (NULL);
3525 
3526                 switch (state) {
3527                 case DTRACE_JSON_REST:
3528                         if (isspace(cc))
3529                                 break;
3530 
3531                         if (cc == '{') {
3532                                 state = DTRACE_JSON_OBJECT;
3533                                 break;
3534                         }
3535 
3536                         if (cc == '[') {
3537                                 in_array = B_TRUE;
3538                                 array_pos = 0;
3539                                 array_elem = dtrace_strtoll(elem, 10, size);
3540                                 found_key = array_elem == 0 ? B_TRUE : B_FALSE;
3541                                 state = DTRACE_JSON_VALUE;
3542                                 break;
3543                         }
3544 
3545                         /*
3546                          * ERROR: expected to find a top-level object or array.
3547                          */
3548                         return (NULL);
3549                 case DTRACE_JSON_OBJECT:
3550                         if (isspace(cc))
3551                                 break;
3552 
3553                         if (cc == '"') {
3554                                 state = DTRACE_JSON_STRING;
3555                                 string_is_key = B_TRUE;
3556                                 break;
3557                         }
3558 
3559                         /*
3560                          * ERROR: either the object did not start with a key
3561                          * string, or we've run off the end of the object
3562                          * without finding the requested key.
3563                          */
3564                         return (NULL);
3565                 case DTRACE_JSON_STRING:
3566                         if (cc == '\\') {
3567                                 *dd++ = '\\';
3568                                 state = DTRACE_JSON_STRING_ESCAPE;
3569                                 break;
3570                         }
3571 
3572                         if (cc == '"') {
3573                                 if (collect_object) {
3574                                         /*
3575                                          * We don't reset the dest here, as
3576                                          * the string is part of a larger
3577                                          * object being collected.
3578                                          */
3579                                         *dd++ = cc;
3580                                         collect_object = B_FALSE;
3581                                         state = DTRACE_JSON_COLLECT_OBJECT;
3582                                         break;
3583                                 }
3584                                 *dd = '\0';
3585                                 dd = dest; /* reset string buffer */
3586                                 if (string_is_key) {
3587                                         if (dtrace_strncmp(dest, elem,
3588                                             size) == 0)
3589                                                 found_key = B_TRUE;
3590                                 } else if (found_key) {
3591                                         if (nelems > 1) {
3592                                                 /*
3593                                                  * We expected an object, not
3594                                                  * this string.
3595                                                  */
3596                                                 return (NULL);
3597                                         }
3598                                         return (dest);
3599                                 }
3600                                 state = string_is_key ? DTRACE_JSON_COLON :
3601                                     DTRACE_JSON_COMMA;
3602                                 string_is_key = B_FALSE;
3603                                 break;
3604                         }
3605 
3606                         *dd++ = cc;
3607                         break;
3608                 case DTRACE_JSON_STRING_ESCAPE:
3609                         *dd++ = cc;
3610                         if (cc == 'u') {
3611                                 escape_unicount = 0;
3612                                 state = DTRACE_JSON_STRING_ESCAPE_UNICODE;
3613                         } else {
3614                                 state = DTRACE_JSON_STRING;
3615                         }
3616                         break;
3617                 case DTRACE_JSON_STRING_ESCAPE_UNICODE:
3618                         if (!isxdigit(cc)) {
3619                                 /*
3620                                  * ERROR: invalid unicode escape, expected
3621                                  * four valid hexidecimal digits.
3622                                  */ 
3623                                 return (NULL);
3624                         }
3625 
3626                         *dd++ = cc;
3627                         if (++escape_unicount == 4)
3628                                 state = DTRACE_JSON_STRING;
3629                         break;
3630                 case DTRACE_JSON_COLON:
3631                         if (isspace(cc))
3632                                 break;
3633 
3634                         if (cc == ':') {
3635                                 state = DTRACE_JSON_VALUE;
3636                                 break;
3637                         }
3638 
3639                         /*
3640                          * ERROR: expected a colon.
3641                          */
3642                         return (NULL);
3643                 case DTRACE_JSON_COMMA:
3644                         if (isspace(cc))
3645                                 break;
3646 
3647                         if (cc == ',') {
3648                                 if (in_array) {
3649                                         state = DTRACE_JSON_VALUE;
3650                                         if (++array_pos == array_elem)
3651                                                 found_key = B_TRUE;
3652                                 } else {
3653                                         state = DTRACE_JSON_OBJECT;
3654                                 }
3655                                 break;
3656                         }
3657 
3658                         /*
3659                          * ERROR: either we hit an unexpected character, or
3660                          * we reached the end of the object or array without
3661                          * finding the requested key.
3662                          */
3663                         return (NULL);
3664                 case DTRACE_JSON_IDENTIFIER:
3665                         if (islower(cc)) {
3666                                 *dd++ = cc;
3667                                 break;
3668                         }
3669 
3670                         *dd = '\0';
3671                         dd = dest; /* reset string buffer */
3672 
3673                         if (dtrace_strncmp(dest, "true", 5) == 0 ||
3674                             dtrace_strncmp(dest, "false", 6) == 0 ||
3675                             dtrace_strncmp(dest, "null", 5) == 0) {
3676                                 if (found_key) {
3677                                         if (nelems > 1) {
3678                                                 /*
3679                                                  * ERROR: We expected an object,
3680                                                  * not this identifier.
3681                                                  */
3682                                                 return (NULL);
3683                                         }
3684                                         return (dest);
3685                                 } else {
3686                                         cur--;
3687                                         state = DTRACE_JSON_COMMA;
3688                                         break;
3689                                 }
3690                         }
3691 
3692                         /*
3693                          * ERROR: we did not recognise the identifier as one
3694                          * of those in the JSON specification.
3695                          */
3696                         return (NULL);
3697                 case DTRACE_JSON_NUMBER:
3698                         if (cc == '.') {
3699                                 *dd++ = cc;
3700                                 state = DTRACE_JSON_NUMBER_FRAC;
3701                                 break;
3702                         }
3703 
3704                         if (cc == 'x' || cc == 'X') {
3705                                 /*
3706                                  * ERROR: specification explicitly excludes
3707                                  * hexidecimal or octal numbers.
3708                                  */
3709                                 return (NULL);
3710                         }
3711 
3712                         /* FALLTHRU */
3713                 case DTRACE_JSON_NUMBER_FRAC:
3714                         if (cc == 'e' || cc == 'E') {
3715                                 *dd++ = cc;
3716                                 state = DTRACE_JSON_NUMBER_EXP;
3717                                 break;
3718                         }
3719 
3720                         if (cc == '+' || cc == '-') {
3721                                 /*
3722                                  * ERROR: expect sign as part of exponent only.
3723                                  */
3724                                 return (NULL);
3725                         }
3726                         /* FALLTHRU */
3727                 case DTRACE_JSON_NUMBER_EXP:
3728                         if (isdigit(cc) || cc == '+' || cc == '-') {

3729                                 *dd++ = cc;
3730                                 break;
3731                         }
3732 
3733                         *dd = '\0';
3734                         dd = dest; /* reset string buffer */
3735                         if (found_key) {
3736                                 if (nelems > 1) {
3737                                         /*
3738                                          * ERROR: We expected an object, not
3739                                          * this number.
3740                                          */
3741                                         return (NULL);
3742                                 }
3743                                 return (dest);
3744                         }
3745 
3746                         cur--;
3747                         state = DTRACE_JSON_COMMA;
3748                         break;
3749                 case DTRACE_JSON_VALUE:
3750                         if (isspace(cc))
3751                                 break;
3752 
3753                         if (cc == '{' || cc == '[') {
3754                                 if (nelems > 1 && found_key) {
3755                                         in_array = cc == '[' ? B_TRUE : B_FALSE;
3756                                         /*
3757                                          * If our element selector directs us
3758                                          * to descend into this nested object,
3759                                          * then move to the next selector
3760                                          * element in the list and restart the
3761                                          * state machine.
3762                                          */
3763                                         while (*elem != '\0')
3764                                                 elem++;
3765                                         elem++; /* skip the inter-element NUL */
3766                                         nelems--;
3767                                         dd = dest;
3768                                         if (in_array) {
3769                                                 state = DTRACE_JSON_VALUE;
3770                                                 array_pos = 0;
3771                                                 array_elem = dtrace_strtoll(
3772                                                     elem, 10, size);
3773                                                 found_key = array_elem == 0 ?
3774                                                     B_TRUE : B_FALSE;
3775                                         } else {
3776                                                 found_key = B_FALSE;
3777                                                 state = DTRACE_JSON_OBJECT;
3778                                         }
3779                                         break;
3780                                 }
3781 
3782                                 /*
3783                                  * Otherwise, we wish to either skip this
3784                                  * nested object or return it in full.
3785                                  */
3786                                 if (cc == '[')
3787                                         brackets = 1;
3788                                 else
3789                                         braces = 1;
3790                                 *dd++ = cc;
3791                                 state = DTRACE_JSON_COLLECT_OBJECT;
3792                                 break;
3793                         }
3794 
3795                         if (cc == '"') {
3796                                 state = DTRACE_JSON_STRING;
3797                                 break;
3798                         }
3799 
3800                         if (islower(cc)) {
3801                                 /*
3802                                  * Here we deal with true, false and null.
3803                                  */
3804                                 *dd++ = cc;
3805                                 state = DTRACE_JSON_IDENTIFIER;
3806                                 break;
3807                         }
3808 
3809                         if (cc == '-' || isdigit(cc)) {
3810                                 *dd++ = cc;
3811                                 state = DTRACE_JSON_NUMBER;
3812                                 break;
3813                         }
3814 
3815                         /*
3816                          * ERROR: unexpected character at start of value.
3817                          */
3818                         return (NULL);
3819                 case DTRACE_JSON_COLLECT_OBJECT:
3820                         if (cc == '\0')
3821                                 /*
3822                                  * ERROR: unexpected end of input.
3823                                  */
3824                                 return (NULL);
3825 
3826                         *dd++ = cc;
3827                         if (cc == '"') {
3828                                 collect_object = B_TRUE;
3829                                 state = DTRACE_JSON_STRING;
3830                                 break;
3831                         }
3832 
3833                         if (cc == ']') {
3834                                 if (brackets-- == 0) {
3835                                         /*
3836                                          * ERROR: unbalanced brackets.
3837                                          */
3838                                         return (NULL);
3839                                 }
3840                         } else if (cc == '}') {
3841                                 if (braces-- == 0) {
3842                                         /*
3843                                          * ERROR: unbalanced braces.
3844                                          */
3845                                         return (NULL);
3846                                 }
3847                         } else if (cc == '{') {
3848                                 braces++;
3849                         } else if (cc == '[') {
3850                                 brackets++;
3851                         }
3852 
3853                         if (brackets == 0 && braces == 0) {
3854                                 if (found_key) {
3855                                         *dd = '\0';
3856                                         return (dest);
3857                                 }
3858                                 dd = dest; /* reset string buffer */
3859                                 state = DTRACE_JSON_COMMA;
3860                         }
3861                         break;
3862                 }
3863         }
3864         return (NULL);
3865 }
3866 
3867 /*
3868  * Emulate the execution of DTrace ID subroutines invoked by the call opcode.
3869  * Notice that we don't bother validating the proper number of arguments or
3870  * their types in the tuple stack.  This isn't needed because all argument
3871  * interpretation is safe because of our load safety -- the worst that can
3872  * happen is that a bogus program can obtain bogus results.
3873  */
3874 static void
3875 dtrace_dif_subr(uint_t subr, uint_t rd, uint64_t *regs,
3876     dtrace_key_t *tupregs, int nargs,
3877     dtrace_mstate_t *mstate, dtrace_state_t *state)
3878 {
3879         volatile uint16_t *flags = &cpu_core[CPU->cpu_id].cpuc_dtrace_flags;


4579 
4580                 if (!dtrace_canload(json, jsonlen + 1, mstate, vstate) ||
4581                     !dtrace_canload(elem, elemlen + 1, mstate, vstate)) {
4582                         regs[rd] = NULL;
4583                         break;
4584                 }
4585 
4586                 if (!DTRACE_INSCRATCH(mstate, jsonlen + 1 + elemlen + 1)) {
4587                         DTRACE_CPUFLAG_SET(CPU_DTRACE_NOSCRATCH);
4588                         regs[rd] = NULL;
4589                         break;
4590                 }
4591 
4592                 /*
4593                  * Read the element selector and split it up into a packed list
4594                  * of strings.
4595                  */
4596                 for (cur = elem; cur < elem + elemlen; cur++) {
4597                         char cc = dtrace_load8(cur);
4598 
4599                         if (cur == elem && cc == '[') {
4600                                 /*
4601                                  * If the first element selector key is
4602                                  * actually an array index then ignore the
4603                                  * bracket.
4604                                  */
4605                                 continue;
4606                         }
4607 
4608                         if (cc == ']')
4609                                 continue;
4610 
4611                         if (cc == '.' || cc == '[') {
4612                                 nelems++;
4613                                 cc = '\0';
4614                         }
4615 
4616                         *ee++ = cc;
4617                 }
4618                 *ee++ = '\0';
4619 
4620                 if ((regs[rd] = (uintptr_t)dtrace_json(size, json, elemlist,
4621                     nelems, dest)) != NULL)
4622                         mstate->dtms_scratch_ptr += jsonlen + 1;
4623                 break;
4624         }
4625 
4626         case DIF_SUBR_TOUPPER: