Skip to content

Commit 5fabf52

Browse files
Merge pull request #349 from jacobwilliams/346-circular-destroy
Destroy a malformed JSON structure
2 parents 9fa4c9e + 0e9f849 commit 5fabf52

File tree

2 files changed

+144
-9
lines changed

2 files changed

+144
-9
lines changed

src/json_value_module.F90

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ module json_value_module
9999
! "value": 0.1E+1
100100
! }
101101
!````
102+
!
103+
!@warning Pointers of this type should only be allocated
104+
! using the methods from [[json_core(type)]].
105+
102106
type,public :: json_value
103107

104108
!force the constituents to be stored contiguously
@@ -2027,6 +2031,17 @@ end subroutine json_value_create
20272031
!@note The original FSON version of this
20282032
! routine was not properly freeing the memory.
20292033
! It was rewritten.
2034+
!
2035+
!@note This routine destroys this variable, it's children, and
2036+
! (if `destroy_next` is true) the subsequent elements in
2037+
! an object or array. It does not destroy the parent or
2038+
! previous elements.
2039+
!
2040+
!@Note There is some protection here to enable destruction of
2041+
! improperly-created linked lists. However, likely there
2042+
! are cases not handled. Use the [[json_value_validate]]
2043+
! method to validate a JSON structure that was manually
2044+
! created using [[json_value]] pointers.
20302045

20312046
recursive subroutine json_value_destroy(json,p,destroy_next)
20322047

@@ -2037,8 +2052,9 @@ recursive subroutine json_value_destroy(json,p,destroy_next)
20372052
logical(LK),intent(in),optional :: destroy_next !! if true, then `p%next`
20382053
!! is also destroyed (default is true)
20392054

2040-
logical(LK) :: des_next
2041-
type(json_value), pointer :: child
2055+
logical(LK) :: des_next !! local copy of `destroy_next` optional argument
2056+
type(json_value), pointer :: child !! for getting child elements
2057+
logical :: circular !! to check to malformed linked lists
20422058

20432059
if (associated(p)) then
20442060

@@ -2052,16 +2068,26 @@ recursive subroutine json_value_destroy(json,p,destroy_next)
20522068

20532069
call destroy_json_data(p)
20542070

2071+
if (associated(p%next)) then
2072+
! check for circular references:
2073+
if (associated(p, p%next)) nullify(p%next)
2074+
end if
2075+
20552076
if (associated(p%children)) then
20562077
do while (p%n_children > 0)
20572078
child => p%children
20582079
if (associated(child)) then
20592080
p%children => p%children%next
20602081
p%n_children = p%n_children - 1
2061-
call json%destroy(child,.false.)
2082+
! check children for circular references:
2083+
circular = (associated(p%children) .and. &
2084+
associated(p%children,child))
2085+
call json%destroy(child,destroy_next=.false.)
2086+
if (circular) exit
20622087
else
2063-
call json%throw_exception('Error in json_value_destroy: '//&
2064-
'Malformed JSON linked list')
2088+
! it is a malformed JSON object. But, we will
2089+
! press ahead with the destroy process, since
2090+
! otherwise, there would be no way to destroy it.
20652091
exit
20662092
end if
20672093
end do
@@ -2075,7 +2101,7 @@ recursive subroutine json_value_destroy(json,p,destroy_next)
20752101
if (associated(p%parent)) nullify(p%parent)
20762102
if (associated(p%tail)) nullify(p%tail)
20772103

2078-
deallocate(p)
2104+
if (associated(p)) deallocate(p)
20792105
nullify(p)
20802106

20812107
end if
@@ -2657,9 +2683,15 @@ recursive subroutine check_if_valid(p,require_parent)
26572683

26582684
! now, check next one:
26592685
if (associated(p%next)) then
2660-
! if it's an element in an
2661-
! array, then require a parent:
2662-
call check_if_valid(p%next,require_parent=.true.)
2686+
if (associated(p,p%next)) then
2687+
error_msg = 'circular linked list'
2688+
is_valid = .false.
2689+
return
2690+
else
2691+
! if it's an element in an
2692+
! array, then require a parent:
2693+
call check_if_valid(p%next,require_parent=.true.)
2694+
end if
26632695
end if
26642696

26652697
if (associated(p%children)) then

src/tests/jf_test_35.F90

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
!*****************************************************************************************
2+
!>
3+
! Module for the 35th unit test.
4+
5+
module jf_test_35_mod
6+
7+
use json_module, rk => json_rk, lk => json_lk, ik => json_ik, ck => json_ck, cdk => json_cdk
8+
use, intrinsic :: iso_fortran_env , only: error_unit, output_unit
9+
10+
implicit none
11+
12+
private
13+
public :: test_35
14+
15+
contains
16+
17+
subroutine test_35(error_cnt)
18+
19+
!! Test destroy a malformed JSON structure
20+
21+
implicit none
22+
23+
integer,intent(out) :: error_cnt
24+
25+
type(json_core) :: json
26+
type(json_value),pointer :: p_root,p_array
27+
logical(LK) :: is_valid !! True if the structure is valid.
28+
character(kind=CK,len=:),allocatable :: error_msg !! error message from `validate`
29+
integer :: i !! counter
30+
31+
error_cnt = 0
32+
33+
write(error_unit,'(A)') ''
34+
write(error_unit,'(A)') '================================='
35+
write(error_unit,'(A)') ' TEST 35'
36+
write(error_unit,'(A)') '================================='
37+
write(error_unit,'(A)') ''
38+
39+
do i = 1, 2
40+
41+
write(error_unit,'(A)') ''
42+
43+
if (i==1) then
44+
call json%create_object(p_root, '')
45+
call json%create_array(p_array, 'array')
46+
call json%add(p_array,'',1)
47+
call json%add(p_root,p_array)
48+
call json%add(p_root,p_array) ! this creates a malformed JSON structure
49+
elseif (i==2) then
50+
call json%create_array(p_array, '')
51+
call json%create_object(p_root, 'object')
52+
call json%add(p_root,'int',1)
53+
call json%add(p_array,p_root)
54+
call json%add(p_array,p_root) ! this creates a malformed JSON structure
55+
! note: below we will destroy p_root, which is the duplicate array element
56+
end if
57+
58+
! test initialize_json_core:
59+
call json%initialize()
60+
61+
call json%print(p_root,error_unit)
62+
63+
! validate it:
64+
call json%validate(p_root,is_valid,error_msg)
65+
if (.not. is_valid) then
66+
write(error_unit,'(A)') 'Validation failed, as expected: '//error_msg
67+
end if
68+
69+
call json%destroy(p_root) ! this crashes with: malloc: *** error
70+
! for object 0x7f9bea500300: pointer being
71+
! freed was not allocated
72+
73+
if (json%failed()) then
74+
call json%print_error_message(error_unit)
75+
error_cnt = error_cnt + 1
76+
else
77+
write(error_unit,'(A)') 'Destroy successful'
78+
end if
79+
write(error_unit,'(A)') ''
80+
81+
end do
82+
83+
end subroutine test_35
84+
85+
end module jf_test_35_mod
86+
!*****************************************************************************************
87+
88+
#ifndef INTERGATED_TESTS
89+
!*****************************************************************************************
90+
program jf_test_35
91+
92+
!! 35th unit test.
93+
94+
use jf_test_35_mod , only: test_35
95+
implicit none
96+
integer :: n_errors
97+
n_errors = 0
98+
call test_35(n_errors)
99+
if (n_errors /= 0) stop 1
100+
101+
end program jf_test_35
102+
!*****************************************************************************************
103+
#endif

0 commit comments

Comments
 (0)